// ------------------------------------------------------------------------------------------
// Licensed by Interprise Solutions.
// http://www.InterpriseSolutions.com
// For details on this license please visit  the product homepage at the URL above.
// THE ABOVE NOTICE MUST REMAIN INTACT.
// ------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Net;
using System.Text;
using System.Web;
using System.Xml;
using System.Xml.Serialization;
using Interprise.Extendable.Base.Facade.Customer.CreditCardGateway;
using Interprise.Extendable.Customer.Facade;
using Interprise.Facade.Customer;
using Interprise.Framework.Base.DatasetComponent;
using Interprise.Framework.Base.Shared;
using Interprise.Framework.Customer.DatasetGateway;
using InterpriseSuiteEcommerceCommon;
using InterpriseSuiteEcommerceCommon.DTO;
using InterpriseSuiteEcommerceCommon.Extensions;
using InterpriseSuiteEcommerceCommon.InterpriseIntegration;
using InterpriseSuiteEcommerceCommon.Tool;

namespace InterpriseSuiteEcommerceGateways
{
    public static class GoogleCheckout
    {

        public static string AddMerchantOrderNumber(string OrderCode)
        {
            gc.AddMerchantOrderNumberRequest gAddMerchantOrderNumberRequest = new gc.AddMerchantOrderNumberRequest();
            gAddMerchantOrderNumberRequest.googleordernumber = GetGoogleOrderNumber(OrderCode);
            gAddMerchantOrderNumberRequest.merchantordernumber = OrderCode;

            return SendRequest(gAddMerchantOrderNumberRequest);
        }

        public static void ProcessChargeAmountNotification(string xmlData)
        {
            gc.ChargeAmountNotification gChargeAmountNotification = new gc.ChargeAmountNotification();
            gChargeAmountNotification = (gc.ChargeAmountNotification)DecodeRequest(xmlData, gChargeAmountNotification.GetType());

            Order thisOrder = GetOrderByGoogleOrderNumber(gChargeAmountNotification.googleordernumber);

            if (thisOrder != null)
            {
                try
                {
                    //See if we have an existing invoice already.
                    string invoiceCode = GetInvoiceCodeForGoogleOrderNumber(gChargeAmountNotification.googleordernumber);

                    if (string.IsNullOrEmpty(invoiceCode))
                    {
                        //No invoice exists so invoice the order.
                        //Invoice the sales order.
                        invoiceCode = Invoice(thisOrder, gChargeAmountNotification.googleordernumber);
                    }
                    else
                    {
                        Logger.WriteLine("processChargeAmountNotification: Invoice Already Exists");
                    }

                    //See if we have an invoice.
                    if (!string.IsNullOrEmpty(invoiceCode))
                    {
                        //See if we have already captured the payment.

                        if (!PaymentIsCaptured(thisOrder, gChargeAmountNotification.googleordernumber, invoiceCode))
                        {
                            Capture(thisOrder, gChargeAmountNotification.googleordernumber, invoiceCode);
                        }
                        else
                        {
                            Logger.WriteLine("processChargeAmountNotification: Payment Already Captured");
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger.WriteLine("processChargeAmountNotification Error: " + ex.ToString());
                }
            }
        }

        private static bool PaymentIsCaptured(Order thisOrder, string p, string invoiceCode)
        {
            ReceiptDatasetGateway gatewayReceiptDataset = new ReceiptDatasetGateway();
            using (ReceiptFacade facadeReceipt = new ReceiptFacade(gatewayReceiptDataset))
            {
                LoadPaymentMethod(invoiceCode, facadeReceipt);

                Logger.WriteLine("PaymentIsCaptured: PaymentMethodView.Count = " + gatewayReceiptDataset.PaymentMethodView.Rows.Count);

                foreach (ReceiptDatasetGateway.PaymentMethodViewRow paymentRow in gatewayReceiptDataset.PaymentMethodView)
                {
                    if (paymentRow.PaymentTypeCode == "Google") //Google/PayPal/Other
                    {
                        Logger.WriteLine("PaymentIsCaptured: PaymentMethodView.CreditCardIsCaptured = " + gatewayReceiptDataset.PaymentMethodView[0].CreditCardIsCaptured.ToString());
                        return gatewayReceiptDataset.PaymentMethodView[0].CreditCardIsCaptured;
                    }
                }

                return false;
            }
        }

        public static void ProcessAuthorizationAmountNotification(string xmlData)
        {
            gc.AuthorizationAmountNotification gAuthorizationAmountNotification = new gc.AuthorizationAmountNotification();
            gAuthorizationAmountNotification = (gc.AuthorizationAmountNotification)DecodeRequest(xmlData, gAuthorizationAmountNotification.GetType());

            try
            {
                //http://code.google.com/apis/checkout/developer/Google_Checkout_XML_API_Notification_API.html#authorization_amount_notification

                Order thisOrder = GetOrderByGoogleOrderNumber(gAuthorizationAmountNotification.googleordernumber);

                ReceiptDatasetGateway gatewayReceiptDataset = new ReceiptDatasetGateway();
                using (ReceiptFacade facadeReceipt = new ReceiptFacade(gatewayReceiptDataset))
                {
                    //Indicate that we are dealing with GoogleCheckout.
                    facadeReceipt.IsGoogleCheckOut = true;

                    //Get the customer CC auth row and related tables.
                    ReceiptDatasetGateway.CustomerCCAuthResponseRow authResponseRow =
                        LoadReceiptRecords(thisOrder.OrderNumber, facadeReceipt);

                    if (authResponseRow != null)
                    {
                        //Update the auth row with auth details..
                        authResponseRow.BeginEdit();
                        authResponseRow.AuthCode = gAuthorizationAmountNotification.serialnumber;
                        authResponseRow.AVSResult = gAuthorizationAmountNotification.avsresponse;
                        authResponseRow.CVResult = gAuthorizationAmountNotification.cvnresponse;

                        Logger.WriteLine("authRow.AuthorizationResult = " + authResponseRow.AuthorizationResult);

                        if (authResponseRow.AuthorizationResult == "Authorization Pending")
                            //The Authorization Result field is set to "Authorization Pending" so
                            //add in the auth expiration date value but leave the place holders for
                            //the eligibleforprotection and buyeraccountage.
                            authResponseRow.AuthorizationResult = string.Format("{0}>{1}>{2}", gAuthorizationAmountNotification.authorizationexpirationdate, "{1}", "{2}");
                        else
                        {
                            //The Authorization Result field is not set to "Authorization Pending"
                            //which means that the risk notification was proccessed frist.
                            //We need to update the place holder with the auth expiration date value.
                            authResponseRow.AuthorizationResult = string.Format(authResponseRow.AuthorizationResult, gAuthorizationAmountNotification.authorizationexpirationdate);
                        }

                        authResponseRow.EndEdit();

                        //Set the CreditCardIsAuthorizedColumn to true.
                        if (gatewayReceiptDataset.PaymentMethodView.Rows.Count > 0)
                        {
                            gatewayReceiptDataset.PaymentMethodView[0].BeginEdit();
                            gatewayReceiptDataset.PaymentMethodView[0].CreditCardIsAuthorized = true;
                            gatewayReceiptDataset.PaymentMethodView[0].EndEdit();
                        }
                        else
                        {
                            Logger.WriteLine("ProcessAuthorizationAmountNotification: Error no PaymentMethodView record found.");
                        }

                        //Commit the PaymentMethod and CC Auth records.
                        string[][] newSet = new string[][] { 
                        new string[] { gatewayReceiptDataset.PaymentMethodView.TableName, 
                            Interprise.Framework.Base.Shared.StoredProcedures.CREATEPAYMENTMETHOD, 
                            Interprise.Framework.Base.Shared.StoredProcedures.UPDATEPAYMENTMETHOD, 
                            Interprise.Framework.Base.Shared.StoredProcedures.DELETEPAYMENTMETHOD }, 
                        new string[] { gatewayReceiptDataset.CustomerCCAuthResponse.TableName, 
                            Interprise.Framework.Base.Shared.StoredProcedures.CREATECUSTOMERCCAUTHRESPONSE, 
                            Interprise.Framework.Base.Shared.StoredProcedures.UPDATECUSTOMERCCAUTHRESPONSE, 
                            Interprise.Framework.Base.Shared.StoredProcedures.DELETECUSTOMERCCAUTHRESPONSE } 
                        };

                        if (!facadeReceipt.UpdateDataSet(newSet
                            , Interprise.Framework.Base.Shared.Enum.TransactionType.CustomerReceipt
                            , string.Format("Update Google Checkout Payment with Auth Info. Sales Order: {0}", thisOrder.OrderNumber)
                            , false))
                        {
                            Logger.WriteLine("ProcessAuthorizationAmountNotification, Error updating PaymentMethodView and CustomerCCAuthResponse, Order No.:" + thisOrder.OrderNumber);
                            WriteViolationsToLog("ProcessAuthorizationAmountNotification", gatewayReceiptDataset);
                        }
                        else
                        {
                            Logger.WriteLine("ProcessAuthorizationAmountNotification, Updated PaymentMethodView and CustomerCCAuthResponse, Order No.:" + thisOrder.OrderNumber);
                        }

                    }
                    else
                    {
                        Logger.WriteLine("ProcessAuthorizationAmountNotification: Error no CustomerCCAuthResponse record found.");
                    }
                }
            }
            catch (Exception e)
            {
                // log exception
                Logger.WriteLine("ProcessAuthorizationAmountNotification Exception: " + e.ToString());
            }
        }

        private static Order GetOrderByGoogleOrderNumber(string strGoogleOrderNumber)
        {
            Order thisOrder = null;

            string strOrderQuery = string.Concat("exec eCommerceGetGoogleOrder @GoogleOrderNumber = ", DB.SQuote(strGoogleOrderNumber));

            using (SqlConnection con = DB.NewSqlConnection())
            {
                con.Open();
                using (IDataReader orderReader = DB.GetRSFormat(con, strOrderQuery))
                {
                    if (orderReader.Read())
                    {
                        thisOrder = new Order(orderReader["OrderNumber"].ToString());
                    }
                }
            }

            return thisOrder;
        }

        private static string GetInvoiceCodeForGoogleOrderNumber(string googleOrderNumber)
        {
            string invoiceCode = null;

            string strQuery = string.Format("SELECT InvoiceCode FROM CustomerInvoice with (NOLOCK) WHERE IsVoided = 0 AND Type = 'Invoice' AND Notes Like '%{0}%'", googleOrderNumber);

            using (SqlConnection con = DB.NewSqlConnection())
            {
                con.Open();
                using (IDataReader invoiceReader = DB.GetRSFormat(con, strQuery))
                {
                    if (invoiceReader.Read())
                    {
                        invoiceCode = invoiceReader["InvoiceCode"].ToString();
                    }
                }
            }

            return invoiceCode;
        }

        private static string Invoice(Order thisOrder, string googleOrderNumber)
        {

            string convertedInvoiceCode = null;

            //Create the sales order facade.
            SalesOrderDatasetGateway gatewaySalesOrderDataset = new SalesOrderDatasetGateway();
            using (SalesOrderFacade facadeSalesOrder = new SalesOrderFacade(gatewaySalesOrderDataset))
            {
                //Load the sales order.
                string[][] parameterSet = new string[][]{
                    new String[] {gatewaySalesOrderDataset.CustomerSalesOrderView.TableName, 
                        StoredProcedures.READCUSTOMERSALESORDER, 
                        "@SalesOrderCode", thisOrder.OrderNumber}, 
                    new String[] {gatewaySalesOrderDataset.CustomerSalesOrderDetailView.TableName, 
                        StoredProcedures.READCUSTOMERSALESORDERDETAIL, 
                        "@SalesOrderCode", thisOrder.OrderNumber}, 
                    new String[] {gatewaySalesOrderDataset.TransactionTaxDetailView.TableName, 
                        StoredProcedures.READTRANSACTIONTAXDETAILVIEW, 
                        "@DocumentCode", thisOrder.OrderNumber},
                    new String[] {gatewaySalesOrderDataset.TransactionItemTaxDetailView.TableName, 
                        StoredProcedures.READTRANSACTIONITEMTAXDETAILVIEW, 
                        "@DocumentCode", thisOrder.OrderNumber},
                    new String[] {gatewaySalesOrderDataset.CustomerItemKitDetailView.TableName, 
                        StoredProcedures.READCUSTOMERITEMKITDETAIL, 
                        "@DocumentCode", thisOrder.OrderNumber},
                    new String[] {gatewaySalesOrderDataset.CustomerSalesRepCommissionView.TableName, 
                        StoredProcedures.READCUSTOMERSALESREPCOMMISSION, 
                        "@DocumentCode", thisOrder.OrderNumber},
                    new String[] {gatewaySalesOrderDataset.CustomerSalesOrderWorkflowView.TableName, 
                        StoredProcedures.READCUSTOMERSALESORDERWORKFLOW, 
                        "@SalesOrderCode", thisOrder.OrderNumber}};
                facadeSalesOrder.LoadDataSet(parameterSet,
                    Interprise.Framework.Base.Shared.Enum.ClearType.None,
                    Interprise.Framework.Base.Shared.Enum.ConnectionStringType.Online);

                gatewaySalesOrderDataset.CustomerSalesOrderWorkflowView[0].Stage = Interprise.Framework.Customer.Shared.Const.WORKFLOW_STAGE_READY_TO_INVOICE;

                DataTable warehouseTable = facadeSalesOrder.InitializeConvertion();
                facadeSalesOrder.SelectAllItemsToInvoice(warehouseTable, true);

                InvoiceDatasetGateway tmpGatewayInvoiceDataset = null;
                string invoiceConversionErrorMessage = string.Empty;

                //Convert the sales order to an invoice.
                if (facadeSalesOrder.ConvertSalesOrderToInvoice(
                        Interprise.Framework.Base.Shared.Enum.TransactionType.Invoice,
                        ref tmpGatewayInvoiceDataset,
                        true,
                        false,
                        ref invoiceConversionErrorMessage))
                {
                    convertedInvoiceCode = tmpGatewayInvoiceDataset.CustomerInvoiceView[0].InvoiceCode;

                }
                else
                {
                    Logger.WriteLine("Invoice: Error unable to ConvertSalesOrderToInvoice; " + invoiceConversionErrorMessage);
                }

            }

            return convertedInvoiceCode;
        }

        private static void Capture(Order thisOrder, string googleOrderNumber, string invoiceCode)
        {
            //Create the invoice facade.
            InvoiceDatasetGateway gatewayInvoiceDataset = new InvoiceDatasetGateway();
            using (InvoiceFacade facadeInvoice = new InvoiceFacade(gatewayInvoiceDataset))
            {
                facadeInvoice.TransactionType = Interprise.Framework.Base.Shared.Enum.TransactionType.Invoice;

                //Load the invoice data.
                string[][] commandSet = new string[][] {  
                    new string[] {Interprise.Framework.Customer.Shared.Const.CUSTOMERINVOICEVIEW_TABLE,
                        "ReadCustomerInvoice", 
                        "@InvoiceCode", 
                        invoiceCode}, 
                    new string[] {Interprise.Framework.Customer.Shared.Const.CUSTOMERINVOICEDETAILVIEW_TABLE,  
                        "ReadCustomerInvoiceDetail", 
                        "@InvoiceCode", 
                        invoiceCode}, 
                    new string[] {Interprise.Framework.Base.Shared.Const.TRANSACTIONTAXDETAILVIEW_TABLE, 
                        "ReadTransactionTaxDetailView", 
                        "@DocumentCode", 
                        invoiceCode}, 
                    new string[] {Interprise.Framework.Base.Shared.Const.TRANSACTIONITEMTAXDETAILVIEW_TABLE,
                        "ReadTransactionItemTaxDetailView", 
                        "@DocumentCode", 
                        invoiceCode}, 
                    new string[] {Interprise.Framework.Customer.Shared.Const.CUSTOMERCREDITALLOCATIONVIEW_TABLE,
                        "ReadCustomerCreditAllocation", 
                        "@DocumentCode", 
                        invoiceCode},
                    new string[] {Interprise.Framework.Customer.Shared.Const.CUSTOMERCREDITVIEW_TABLE,
                        "ReadCustomerCredit", 
                        "@CreditCode", 
                        invoiceCode}, 
                    new string[] {Interprise.Framework.Customer.Shared.Const.CUSTOMERITEMKITDETAILVIEW_TABLE,
                        "ReadCustomerItemKitDetail", 
                        "@DocumentCode", 
                        invoiceCode}, 
                    new string[] {Interprise.Framework.Customer.Shared.Const.CUSTOMERSALESREPCOMMISSIONVIEW_TABLE,
                        "ReadCustomerSalesRepCommission", 
                        "@DocumentCode", 
                        invoiceCode}};
                facadeInvoice.LoadDataSet(commandSet,
                    Interprise.Framework.Base.Shared.Enum.ClearType.None,
                    Interprise.Framework.Base.Shared.Enum.ConnectionStringType.Online);

                //Create the receipt facade.
                ReceiptDatasetGateway gatewayReceiptDataset = new ReceiptDatasetGateway();
                using (ReceiptFacade facadeReceipt = new ReceiptFacade(gatewayReceiptDataset))
                {
                    //Indicate that we are dealing with GoogleCheckout.
                    facadeReceipt.IsGoogleCheckOut = true;

                    //Load the receipt records.
                    LoadReceiptRecords(thisOrder.OrderNumber, facadeReceipt);

                    //Set the tran type to Capture.
                    gatewayReceiptDataset.PaymentMethodView[0].CreditCardTransactionType = "Capture";

                    //Create the transaction receipt facade.
                    TransactionReceiptDatasetGateway gatewayTransactionReceiptDatasetInvoice = new TransactionReceiptDatasetGateway();
                    using (TransactionReceiptFacade facadeTransactionReceiptInvoice = new TransactionReceiptFacade(gatewayTransactionReceiptDatasetInvoice))
                    {
                        facadeTransactionReceiptInvoice.ParentTransactionFacade = facadeInvoice;

                        //Load the tran receipt invoice data.
                        string[][] loadTransactionTables = new string[][]{
                            new string[] { 
                                TransactionReceiptDatasetGateway.CUSTOMERTRANSACTIONRECEIPTVIEW_TABLE,
                                "ReadCustomerTransactionReceipt", 
                                "@DocumentCode", 
                                invoiceCode}};
                        facadeTransactionReceiptInvoice.LoadDataSet(loadTransactionTables,
                            Interprise.Framework.Base.Shared.Enum.ClearType.Specific,
                            Interprise.Framework.Base.Shared.Enum.ConnectionStringType.Online);

                        //Get the receipt code.
                        string[] receiptCodes = new string[1];
                        receiptCodes[0] = gatewayReceiptDataset.PaymentMethodView[0].DocumentCode;

                        //Create the cc facade wrapper.
                        using (CreditCardAuthorizationFacadeWrapper facadeCardCapture = new CreditCardAuthorizationFacadeWrapper())
                        {
                            facadeCardCapture.TransactionType = facadeReceipt.TransactionType;
                            facadeCardCapture.TransactionReceiptFacade = facadeTransactionReceiptInvoice;
                            facadeCardCapture.CurrentCardPayment = receiptCodes[0];
                            facadeCardCapture.CurrentParentTransactionCode = invoiceCode;

                            //Create the goolge gateway.
                            GoogleCreditCardGateway googleGateway = new GoogleCreditCardGateway(
                                googleOrderNumber,
                                gatewayReceiptDataset.CustomerCCAuthResponse[0].AuthorizationResult,
                                gatewayReceiptDataset.CustomerCCAuthResponse[0].AuthCode,
                                gatewayReceiptDataset.CustomerCCAuthResponse[0].AVSResult,
                                gatewayReceiptDataset.CustomerCCAuthResponse[0].CVResult);

                            //Capture the google payment.
                            string captureResult = string.Empty;
                            facadeCardCapture.AuthorizeCreditCardWrapper(googleGateway, facadeReceipt,
                                gatewayReceiptDataset.PaymentMethodView[0], "Capture", ref captureResult);

                            //Convert the card payment.
                            facadeCardCapture.ConvertCardPaymentToReceiptWrapper(receiptCodes, gatewayReceiptDataset);

                            //Merge the transaction receipt into the invoice dataset.
                            gatewayInvoiceDataset.Merge(gatewayTransactionReceiptDatasetInvoice.CustomerTransactionReceiptView);

                            Logger.WriteLine("CustomerTransactionReceiptView.ReceivableCode = " + gatewayTransactionReceiptDatasetInvoice.CustomerTransactionReceiptView[0].ReceivableCode);

                            //Update the receipt data.
                            string[][] receiptCommands = new string[][] { 
                                new string[] { gatewayReceiptDataset.CustomerCardPayment.TableName, 
                                    Interprise.Framework.Base.Shared.StoredProcedures.CREATECUSTOMERCARDPAYMENT, 
                                    Interprise.Framework.Base.Shared.StoredProcedures.UPDATECUSTOMERCARDPAYMENT, 
                                    Interprise.Framework.Base.Shared.StoredProcedures.DELETECUSTOMERCARDPAYMENT },
                                new string[] { gatewayReceiptDataset.CustomerPayment.TableName, 
                                    Interprise.Framework.Base.Shared.StoredProcedures.CREATECUSTOMERPAYMENT, 
                                    Interprise.Framework.Base.Shared.StoredProcedures.UPDATECUSTOMERPAYMENT, 
                                    Interprise.Framework.Base.Shared.StoredProcedures.DELETECUSTOMERPAYMENT },
                                new string[] { gatewayReceiptDataset.PaymentMethodView.TableName, 
                                    Interprise.Framework.Base.Shared.StoredProcedures.CREATEPAYMENTMETHOD, 
                                    Interprise.Framework.Base.Shared.StoredProcedures.UPDATEPAYMENTMETHOD, 
                                    Interprise.Framework.Base.Shared.StoredProcedures.DELETEPAYMENTMETHOD }, 
                                new string[] { gatewayReceiptDataset.CustomerCCAuthResponse.TableName, 
                                    Interprise.Framework.Base.Shared.StoredProcedures.CREATECUSTOMERCCAUTHRESPONSE, 
                                    Interprise.Framework.Base.Shared.StoredProcedures.UPDATECUSTOMERCCAUTHRESPONSE, 
                                    Interprise.Framework.Base.Shared.StoredProcedures.DELETECUSTOMERCCAUTHRESPONSE } 
                                };

                            if (!facadeReceipt.UpdateDataSet(receiptCommands
                                , Interprise.Framework.Base.Shared.Enum.TransactionType.CustomerReceipt
                                , string.Empty, false))
                            {
                                Logger.WriteLine("Capture, Error updating receipt, Order No.:" + thisOrder.OrderNumber);
                                WriteViolationsToLog("Capture", gatewayReceiptDataset);
                            }
                            else
                            {
                                Logger.WriteLine("Capture, Updated receipt, Order No.:" + thisOrder.OrderNumber);
                            }


                            //Refresh the in memory payment tables with the new values.
                            facadeReceipt.RefreshPaymentTable(facadeCardCapture.NewReceiptDataTable, facadeCardCapture.NewPaymentMethodDataTable);

                            Logger.WriteLine("PaymentMethodView.DocumentCode = " + gatewayReceiptDataset.PaymentMethodView[0].DocumentCode);

                            //Add in the CC number to the payment record.
                            gatewayReceiptDataset.PaymentMethodView[0].BeginEdit();
                            gatewayReceiptDataset.PaymentMethodView[0].CreditCard = facadeReceipt.EncryptCardNumber(gatewayReceiptDataset.CustomerCCAuthResponse[0].CreditCard, gatewayReceiptDataset.PaymentMethodView[0]);
                            gatewayReceiptDataset.PaymentMethodView[0].CreditCardOnFile = gatewayReceiptDataset.CustomerCCAuthResponse[0].CreditCard;
                            gatewayReceiptDataset.PaymentMethodView[0].EndEdit();

                            //Update the receipt records.
                            if (!facadeReceipt.UpdateDataSet(receiptCommands
                                , Interprise.Framework.Base.Shared.Enum.TransactionType.CustomerReceipt
                                , string.Empty, false))
                            {
                                Logger.WriteLine("Capture, Error updating receipt, Order No.:" + thisOrder.OrderNumber);
                                WriteViolationsToLog("Capture", gatewayReceiptDataset);
                            }
                            else
                            {
                                Logger.WriteLine("Capture, Updated receipt, Order No.:" + thisOrder.OrderNumber);
                            }

                            //Update the invoice data.
                            string errorCode = string.Empty;
                            string[][] invoiceCommands = facadeInvoice.CreateParameterSet(
                                facadeInvoice.get_RelatedTables(Interprise.Framework.Base.Shared.Enum.TransactionType.Invoice));
                            
                            if(!facadeInvoice.UpdateDataSet(invoiceCommands,
                                Interprise.Framework.Base.Shared.Enum.TransactionType.Invoice, string.Empty, false))
                            {
                                Logger.WriteLine("Capture, Error updating invoice, Order No.:" + thisOrder.OrderNumber);
                                WriteViolationsToLog("Capture", gatewayInvoiceDataset);
                            }
                            else
                            {
                                Logger.WriteLine("Capture, Updated invoice, Order No.:" + thisOrder.OrderNumber);
                            }

                            //Create a BO if we need to.
                            if (facadeInvoice.IsCreateBackOrder())
                            {
                                string boCodes = string.Empty;
                                bool backOrderCreated = facadeInvoice.CreateBackOrder(ref boCodes);
                            }

                            //Update the receipt invoice data.
                            facadeTransactionReceiptInvoice.ReserveAndAllocateReceipt(gatewayInvoiceDataset,
                                facadeReceipt, "Invoice");
                            if (!facadeInvoice.UpdateDataSet(invoiceCommands,
                                Interprise.Framework.Base.Shared.Enum.TransactionType.Invoice, string.Empty, false))
                            {
                                Logger.WriteLine("Capture, Error updating invoice, Order No.:" + thisOrder.OrderNumber);
                                WriteViolationsToLog("Capture", gatewayInvoiceDataset);
                            }
                            else
                            {
                                Logger.WriteLine("Capture, Updated invoice, Order No.:" + thisOrder.OrderNumber);
                            }
                        }
                    }
                }
            }
        }

        public static void ProcessUnknownNotification(string xmlData)
        {
            Logger.WriteLine("Unknown Notification: " + xmlData);
        }

        private static string GetCountryCodeFromTwoLetterISOCode(string isoCode)
        {
            string countryCode = string.Empty;

            using (SqlConnection con = DB.NewSqlConnection())
            {
                con.Open();
                using (IDataReader reader = DB.GetRSFormat(con, "SELECT CountryCode FROM SystemCountry with (NOLOCK) WHERE ISOCode = {0}", DB.SQuote(isoCode)))
                {
                    if (reader.Read())
                    {
                        countryCode = DB.RSField(reader, "CountryCode");
                    }
                }
            }

            return countryCode;
        }

        public static void ProcessOrderStateChangeNotification(string xmlData)
        {
            try
            {
                gc.OrderStateChangeNotification gOrderStateChangeNotification = new gc.OrderStateChangeNotification();
                gOrderStateChangeNotification = (gc.OrderStateChangeNotification)DecodeRequest(xmlData, gOrderStateChangeNotification.GetType());
                Order thisOrder = GetOrderByGoogleOrderNumber(gOrderStateChangeNotification.googleordernumber);

                //Log the financial and fulfillment order states
                Logger.WriteLine("ProcessOrderStateChangeNotification: FinancialOrderState = " + gOrderStateChangeNotification.newfinancialorderstate.ToString());
                Logger.WriteLine("ProcessOrderStateChangeNotification: FulfillmentOrderState = " + gOrderStateChangeNotification.newfulfillmentorderstate.ToString());

                if (thisOrder != null)
                {
                    //Check the financial order state value.
                    switch (gOrderStateChangeNotification.newfinancialorderstate)
                    {
                        case InterpriseSuiteEcommerceGateways.gc.FinancialOrderState.CANCELLED_BY_GOOGLE:
                        case InterpriseSuiteEcommerceGateways.gc.FinancialOrderState.CANCELLED:
                            //ProcessOrderCancelled(thisOrder);
                            Logger.WriteLine("CANCELLED!" + Environment.NewLine);
                            break;
                        case InterpriseSuiteEcommerceGateways.gc.FinancialOrderState.PAYMENT_DECLINED:
                            ProcessPaymentDeclined(thisOrder);
                            Logger.WriteLine("PAYMENT_DECLINED!" + Environment.NewLine);
                            break;
                        case InterpriseSuiteEcommerceGateways.gc.FinancialOrderState.CHARGEABLE:
                            //Only send the receipt when the order is "NEW".
                            if (gOrderStateChangeNotification.newfulfillmentorderstate
                                == InterpriseSuiteEcommerceGateways.gc.FulfillmentOrderState.NEW)
                            {
                                //The order is chargable so send the receipt.
                                SendReceipt(thisOrder);
                            }
                            Logger.WriteLine("ORDER IS CHARGEABLE" + Environment.NewLine);
                            break;
                    }

                    //Check the fulfillment order stat value.
                    switch (gOrderStateChangeNotification.newfulfillmentorderstate)
                    {
                        case InterpriseSuiteEcommerceGateways.gc.FulfillmentOrderState.DELIVERED:
                            ProcessOrderDelivered(thisOrder);
                            break;
                    }
                }
                else
                {
                    Logger.WriteLine("ProcessOrderStateChangeNotification Error: Google order not found. Order number: " + gOrderStateChangeNotification.googleordernumber);
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLine("ProcessOrderStateChangeNotification Error: " + ex.ToString());
            }
        }

        private static void SendReceipt(Order thisOrder)
        {
            //Check the SendStoreReceipt to see if we should send an order receipt.
            if (AppLogic.AppConfigBool("GoogleCheckout.SendStoreReceipt"))
            {
                InterpriseShoppingCart cart = InterpriseShoppingCart.Get(thisOrder.Customer, CartTypeEnum.ShoppingCart, true);
                Customer thisCustomer = thisOrder.Customer;

                //I couldn't find any way to tie an order back to an anon customer record (EcommerceCustomer table).
                //So inorder to be able to make sure we can e-mail the customer the receipt we will 
                //get the Email address off of the PaymentMethod record. If for some reason the e-mail address
                //is empty we will then just attempt to use the one on the customer from the order.
                string emailAddress = GetEmailFromPayment(thisOrder.OrderNumber);
                if (!string.IsNullOrEmpty(emailAddress))
                {
                    thisCustomer.EMail = emailAddress;
                }

                AppLogic.SendOrderEMail(thisOrder.Customer, cart, thisOrder.OrderNumber, false, thisOrder.PaymentMethod, true, false);
            }
        }

        private static string GetEmailFromPayment(string orderNumber)
        {
            ReceiptDatasetGateway gatewayReceiptDataset = new ReceiptDatasetGateway();
            using (ReceiptFacade facadeReceipt = new ReceiptFacade(gatewayReceiptDataset))
            {
                //Get the receipt records.
                LoadReceiptRecords(orderNumber, facadeReceipt);

                return gatewayReceiptDataset.PaymentMethodView[0].CreditCardEmail;
            }
        }

        /// <summary>
        /// Complete the orders life cycle.
        /// </summary>
        /// <param name="thisOrder">The order that was completed.</param>
        private static void ProcessOrderDelivered(Order thisOrder)
        {
            try
            {
                // Process Order Delivered
                // TBD: Provide implementation here...    
                // http://code.google.com/apis/checkout/developer/Google_Checkout_XML_API_Notification_API.html
            }
            catch (Exception ex)
            {
                Logger.WriteLine("ProcessOrderDelivered Error: " + ex.ToString());
            }
        }

        /// <summary>
        /// Process the order that was cancelled either by Google or by the customer. 
        /// </summary>
        /// <param name="thisOrder">The order created by Google.</param>
        private static void ProcessOrderCancelled(Order thisOrder)
        {
            try
            {
                // Process Order Cancelled
                // TBD: Provide implementation here...    
                // http://code.google.com/apis/checkout/developer/Google_Checkout_XML_API_Notification_API.html

                // Added codes starts here

                SalesOrderDatasetGateway gatewaySalesOrderDataset = new SalesOrderDatasetGateway();
                SalesOrderFacade facadeSalesOrder = new SalesOrderFacade(gatewaySalesOrderDataset);

                string[][] parameterSet = new string[][]{new String[] {gatewaySalesOrderDataset.CustomerSalesOrderView.TableName, Interprise.Framework.Base.Shared.StoredProcedures.READCUSTOMERSALESORDER, "@SalesOrderCode", thisOrder.OrderNumber}, 
                                        new String[] {gatewaySalesOrderDataset.CustomerSalesOrderDetailView.TableName, Interprise.Framework.Base.Shared.StoredProcedures.READCUSTOMERSALESORDERDETAIL, "@SalesOrderCode", thisOrder.OrderNumber}, 
                                       new String[] {gatewaySalesOrderDataset.CustomerSalesOrderWorkflowView.TableName, Interprise.Framework.Base.Shared.StoredProcedures.READCUSTOMERSALESORDERWORKFLOW, "@SalesOrderCode", thisOrder.OrderNumber}};

                facadeSalesOrder.LoadDataSet(parameterSet, Interprise.Framework.Base.Shared.Enum.ClearType.None, Interprise.Framework.Base.Shared.Enum.ConnectionStringType.Online);
                facadeSalesOrder.TransactionType = Interprise.Framework.Base.Shared.Enum.TransactionType.SalesOrder;

                facadeSalesOrder.VoidOrder();

                if (!facadeSalesOrder.UpdateDataSet(facadeSalesOrder.CreateParameterSet(facadeSalesOrder.get_RelatedTables(facadeSalesOrder.TransactionType)),
                                            facadeSalesOrder.TransactionType,
                                            string.Empty,
                                            false))
                {
                    Logger.WriteLine("facadeReceipt.UpdateDataSet ERROR Order Number: " + thisOrder.OrderNumber);
                    WriteViolationsToLog("ProcessOrderCancelled", gatewaySalesOrderDataset);
                }
                else
                {
                    Logger.WriteLine(string.Format("Sales Order No.: {0} was successfully cancelled.", thisOrder.OrderNumber));
                }

                // end added

            }
            catch (Exception ex)
            {
                Logger.WriteLine("ProcessOrderCancelled Error: " + ex.ToString());
            }
        }

        /// <summary>
        /// Process the order when the payment is declined.
        /// </summary>
        /// <param name="thisOrder">The order created by Google.</param>
        private static void ProcessPaymentDeclined(Order thisOrder)
        {
            try
            {
                // Process Payment Declined
                // TBD: Provide implementation here...    
                // http://code.google.com/apis/checkout/developer/Google_Checkout_XML_API_Notification_API.html

                SalesOrderDatasetGateway gatewaySalesOrderDataset = new SalesOrderDatasetGateway();
                SalesOrderFacade facadeSalesOrder = new SalesOrderFacade(gatewaySalesOrderDataset);

                string[][] parameterSet = new string[][]{new String[] {gatewaySalesOrderDataset.CustomerSalesOrderView.TableName, Interprise.Framework.Base.Shared.StoredProcedures.READCUSTOMERSALESORDER, "@SalesOrderCode", thisOrder.OrderNumber}, 
                                        new String[] {gatewaySalesOrderDataset.CustomerSalesOrderDetailView.TableName, Interprise.Framework.Base.Shared.StoredProcedures.READCUSTOMERSALESORDERDETAIL, "@SalesOrderCode", thisOrder.OrderNumber}, 
                                       new String[] {gatewaySalesOrderDataset.CustomerSalesOrderWorkflowView.TableName, Interprise.Framework.Base.Shared.StoredProcedures.READCUSTOMERSALESORDERWORKFLOW, "@SalesOrderCode", thisOrder.OrderNumber}};

                facadeSalesOrder.LoadDataSet(parameterSet, Interprise.Framework.Base.Shared.Enum.ClearType.None, Interprise.Framework.Base.Shared.Enum.ConnectionStringType.Online);
                facadeSalesOrder.TransactionType = Interprise.Framework.Base.Shared.Enum.TransactionType.SalesOrder;

                foreach (DataRow dRow in gatewaySalesOrderDataset.CustomerSalesOrderWorkflowView.Rows)
                {
                    dRow[gatewaySalesOrderDataset.CustomerSalesOrderWorkflowView.StageColumn.ColumnName] = Interprise.Framework.Customer.Shared.Const.WORKFLOW_STAGE_APPROVE_CREDIT;
                }

                if (!facadeSalesOrder.UpdateDataSet(facadeSalesOrder.CreateParameterSet(facadeSalesOrder.get_RelatedTables(facadeSalesOrder.TransactionType)),
                                            facadeSalesOrder.TransactionType,
                                            string.Empty,
                                            false))
                {
                    Logger.WriteLine("facadeReceipt.UpdateDataSet ERROR Order Number: " + thisOrder.OrderNumber);
                    WriteViolationsToLog("ProcessPaymentDeclined", gatewaySalesOrderDataset);
                }
                else
                {
                    Logger.WriteLine(string.Format("Sales Order No.: {0} workflow status was automatically set to APPROVE CREDIT.", thisOrder.OrderNumber));
                }

            }
            catch (Exception ex)
            {
                Logger.WriteLine("ProcessPaymentDeclined Error: " + ex.ToString());
            }
        }

        private static void AddInitalPayment(string orderNumber, Customer thisCustomer, gc.NewOrderNotification newOrderNotification)
        {
            //Create the transaction receipt facade.
            TransactionReceiptDatasetGateway transactionReceiptDataset = new TransactionReceiptDatasetGateway();
            using (ITransactionReceiptInterface facadeTransactionReceipt = new TransactionReceiptFacade(transactionReceiptDataset))
            {

                //Create the receipt facade.
                ReceiptDatasetGateway gatewayReceiptDataset = new ReceiptDatasetGateway();
                using (ReceiptFacade facadeReceipt = new ReceiptFacade(gatewayReceiptDataset))
                {
                    //Indicate that we are dealing with GoogleCheckout.
                    facadeReceipt.IsGoogleCheckOut = true;


                    //Create the sales order facade.
                    SalesOrderDatasetGateway gatewaySalesOrderDataset = new SalesOrderDatasetGateway();
                    using (SalesOrderFacade facadeSalesOrder = new SalesOrderFacade(gatewaySalesOrderDataset))
                    {

                        //Load up the sales order dataset gateway with the customer sales order data.
                        string[][] parameterSet = new string[][]{
                            new String[] {gatewaySalesOrderDataset.CustomerSalesOrderView.TableName, 
                                StoredProcedures.READCUSTOMERSALESORDER, "@SalesOrderCode", orderNumber}, 
                            new String[] {gatewaySalesOrderDataset.CustomerSalesOrderDetailView.TableName, 
                                StoredProcedures.READCUSTOMERSALESORDERDETAIL, "@SalesOrderCode", orderNumber}};

                        facadeSalesOrder.LoadDataSet(parameterSet, Interprise.Framework.Base.Shared.Enum.ClearType.None, Interprise.Framework.Base.Shared.Enum.ConnectionStringType.Online);
                        facadeSalesOrder.TransactionType = Interprise.Framework.Base.Shared.Enum.TransactionType.SalesOrder;

                        //Get the sales order record.
                        DataRow[] salesOrderRow = new DataRow[] { gatewaySalesOrderDataset.CustomerSalesOrderView[0] };

                        if (gatewaySalesOrderDataset.CustomerSalesOrderView.Rows.Count > 0)
                        {
                            if (gatewaySalesOrderDataset.CustomerSalesOrderView[0].PaymentTermCode != Interprise.Framework.Base.Shared.Const.PAYMENT_METHOD_CREDIT_CARD)
                            {
                                gatewaySalesOrderDataset.CustomerSalesOrderView[0].BeginEdit();
                                gatewaySalesOrderDataset.CustomerSalesOrderView[0].PaymentTermCode = Interprise.Framework.Base.Shared.Const.PAYMENT_METHOD_CREDIT_CARD;
                                gatewaySalesOrderDataset.CustomerSalesOrderView[0].PaymentTerm = Interprise.Framework.Base.Shared.Const.PAYMENT_METHOD_CREDIT_CARD;
                                gatewaySalesOrderDataset.CustomerSalesOrderView[0].EndEdit();
                            }
                        }

                        //Set the receipt transaction type to customer receipt.
                        facadeReceipt.TransactionType = Interprise.Framework.Base.Shared.Enum.TransactionType.CustomerReceipt;

                        //Add the receipt for the sales order.
                        facadeReceipt.AddReceipt(salesOrderRow
                            , Interprise.Framework.Base.Shared.Enum.TransactionType.CustomerReceipt
                            , true
                            , gatewaySalesOrderDataset.CustomerSalesOrderView[0].Total
                            , gatewaySalesOrderDataset.CustomerSalesOrderView[0].PaymentTermCode
                            , gatewaySalesOrderDataset.CustomerSalesOrderView[0].SalesOrderCode
                            , gatewaySalesOrderDataset.CustomerSalesOrderView[0].ContactCode);

                        facadeReceipt.AssignCreditCardTransactionType(gatewayReceiptDataset.PaymentMethodView[0]
                            , Interprise.Framework.Base.Shared.Enum.CreditCardTransaction.AuthorizeOnly);

                        //Update related receipt tables.
                        string[] receiptRelatedTables = facadeReceipt.get_RelatedTables(Interprise.Framework.Base.Shared.Enum.TransactionType.CustomerReceipt);
                        string[][] receiptCommands = facadeReceipt.CreateParameterSet(receiptRelatedTables);

                        if (!facadeReceipt.UpdateDataSet(receiptCommands,
                                                    Interprise.Framework.Base.Shared.Enum.TransactionType.CustomerReceipt,
                                                    string.Format("Added Receipt: Sales Order - {0} - {1}",
                                                        gatewaySalesOrderDataset.CustomerSalesOrderView[0].SalesOrderCode,
                                                        gatewaySalesOrderDataset.CustomerSalesOrderView[0].ContactFullName),
                                                    false))
                        {
                            Logger.WriteLine("AddInitalPayment, Error updating Receipt, Order No.: " + orderNumber);
                            WriteViolationsToLog( "AddInitalPayment", gatewayReceiptDataset);
                        }
                        else
                        {
                            Logger.WriteLine("AddInitalPayment Added (Receipt): " + orderNumber + Environment.NewLine + " Receivable Code: " + gatewayReceiptDataset.PaymentMethodView[0].DocumentCode + " Payment amount: " + gatewaySalesOrderDataset.CustomerSalesOrderView[0].Total.ToString());
                        }

                        int dataSourceRowIndex = 0;

                        //Assign the transaction receipt.
                        facadeTransactionReceipt.AssignTransactionReceipt(orderNumber
                            , (Interprise.Extendable.Customer.Facade.IReceiptInterface)facadeReceipt
                            , ref dataSourceRowIndex);

                        //Update the sales order.
                        //updating the balance of the sales order after linking the order to the payment
                        facadeSalesOrder.ComputeTotals(true, false, false);
                        gatewaySalesOrderDataset.Merge(facadeTransactionReceipt.CurrentDataset);

                        string[] relatedTables = facadeSalesOrder.get_RelatedTables(facadeSalesOrder.TransactionType);
                        string[][] commandSet = facadeSalesOrder.CreateParameterSet(relatedTables);

                        if (!facadeSalesOrder.UpdateDataSet(commandSet
                            , facadeSalesOrder.TransactionType
                            , string.Format("Sales Order - {0} - {1}",
                                gatewaySalesOrderDataset.CustomerSalesOrderView[0].SalesOrderCode,
                                gatewaySalesOrderDataset.CustomerSalesOrderView[0].ContactFullName)
                            , false))
                        {
                            Logger.WriteLine("AddInitalPayment, Error updating Order, Order No.:" + orderNumber);
                            WriteViolationsToLog("AddInitalPayment", gatewaySalesOrderDataset);
                        }
                        else
                        {
                            Logger.WriteLine("AddInitalPayment (Sales Order): " + orderNumber);
                        }


                        //Create a cc auth row and add the details.
                        var ccAuthResponseRow = gatewayReceiptDataset.CustomerCCAuthResponse.NewCustomerCCAuthResponseRow();
                        ccAuthResponseRow.ResponseCode = facadeReceipt.GenerateDocumentCode(Interprise.Framework.Base.Shared.Enum.TransactionType.CCResponse, null);
                        ccAuthResponseRow.CustomerCode = thisCustomer.CustomerCode;
                        ccAuthResponseRow.DocumentCode = gatewayReceiptDataset.CustomerCardPayment[0].CardPaymentCode;//Convert.ToString(facadeTransactionReceipt.CurrentDataset.Tables["CustomerTransactionReceiptView"].Rows[0]["ReceivableCode"]);
                        ccAuthResponseRow.TransactionType = "Auth";
                        ccAuthResponseRow.Amount = gatewayReceiptDataset.PaymentMethodView[0].AmountPaidRate;
                        ccAuthResponseRow.ResponseMsg = "Google Checkout";
                        ccAuthResponseRow.AuthorizationResult = "Authorization Pending";
                        ccAuthResponseRow.GatewayResponseCode = ccAuthResponseRow.ResponseCode;
                        ccAuthResponseRow.Result = 0; //authorization is good
                        ccAuthResponseRow.Message = "Authorization Pending";
                        ccAuthResponseRow.Reference = newOrderNotification.googleordernumber;

                        gatewayReceiptDataset.CustomerCCAuthResponse.AddCustomerCCAuthResponseRow(ccAuthResponseRow);
                        
                        String GoogleAccountCode = Interprise.Facade.Base.SimpleFacade.Instance.GetField("DefaultBankAccountCode", "SystemPaymentType", "PaymentMethodCode='Web Checkout' AND IsActive=1");
                        if (GoogleAccountCode.Length == 0)
                        {
                            GoogleAccountCode = gatewayReceiptDataset.PaymentMethodView[0].CheckbookCode.ToString();
                        }
                        //Update the payment method with the correct details.
                        gatewayReceiptDataset.PaymentMethodView[0].BeginEdit();
                        gatewayReceiptDataset.PaymentMethodView[0][gatewayReceiptDataset.PaymentMethodView.CheckbookCodeColumn.ColumnName] = GoogleAccountCode;
                        gatewayReceiptDataset.PaymentMethodView[0][gatewayReceiptDataset.PaymentMethodView.PaymentMethodColumn.ColumnName] = "Web Checkout";
                        gatewayReceiptDataset.PaymentMethodView[0][gatewayReceiptDataset.PaymentMethodView.PaymentTypeCodeColumn.ColumnName] = "Google"; //Google/PayPal/Other
                        gatewayReceiptDataset.PaymentMethodView[0][gatewayReceiptDataset.PaymentMethodView.PaymentTypeDescriptionColumn.ColumnName] = "Google"; //Google/PayPal/Other
                        gatewayReceiptDataset.PaymentMethodView[0][gatewayReceiptDataset.PaymentMethodView.CreditCardReferenceColumn.ColumnName] = newOrderNotification.googleordernumber;
                        gatewayReceiptDataset.PaymentMethodView[0][gatewayReceiptDataset.PaymentMethodView.CreditCardResponseCodeColumn.ColumnName] = ccAuthResponseRow.ResponseCode;
                        gatewayReceiptDataset.PaymentMethodView[0][gatewayReceiptDataset.PaymentMethodView.CreditCardIsAuthorizedColumn.ColumnName] = true;
                        gatewayReceiptDataset.PaymentMethodView[0][gatewayReceiptDataset.PaymentMethodView.CreditCardIsVoidedColumn.ColumnName] = false;
                        gatewayReceiptDataset.PaymentMethodView[0][gatewayReceiptDataset.PaymentMethodView.CreditCardIsCreditedColumn.ColumnName] = false;
                        gatewayReceiptDataset.PaymentMethodView[0][gatewayReceiptDataset.PaymentMethodView.CreditCardEmailColumn.ColumnName] = newOrderNotification.buyerbillingaddress.email;
                        gatewayReceiptDataset.PaymentMethodView[0].EndEdit();

                        //Commit the PaymentMethod and CC Auth records.
                        string[][] newSet = new string[][] { 
                            new string[] { gatewayReceiptDataset.PaymentMethodView.TableName, 
                                Interprise.Framework.Base.Shared.StoredProcedures.CREATEPAYMENTMETHOD, 
                                Interprise.Framework.Base.Shared.StoredProcedures.UPDATEPAYMENTMETHOD, 
                                Interprise.Framework.Base.Shared.StoredProcedures.DELETEPAYMENTMETHOD }, 
                            new string[] { gatewayReceiptDataset.CustomerCCAuthResponse.TableName, 
                                Interprise.Framework.Base.Shared.StoredProcedures.CREATECUSTOMERCCAUTHRESPONSE, 
                                Interprise.Framework.Base.Shared.StoredProcedures.UPDATECUSTOMERCCAUTHRESPONSE, 
                                Interprise.Framework.Base.Shared.StoredProcedures.DELETECUSTOMERCCAUTHRESPONSE }};

                        if (!facadeReceipt.UpdateDataSet(newSet
                            , Interprise.Framework.Base.Shared.Enum.TransactionType.CustomerReceipt
                            , string.Format("Created Google Checkout Payment. Sales Order: {0}", orderNumber)
                            , false))
                        {
                            Logger.WriteLine("AddInitalPayment, Error updating CustomerCCAuthResponse, Order No.:" + orderNumber);
                            WriteViolationsToLog("AddInitalPayment", gatewayReceiptDataset);
                        }
                        else
                        {
                            Logger.WriteLine("AddInitalPayment, Created CustomerCCAuthResponse, Order No.:" + orderNumber);
                        }
                    }
                }
            }
        }

        private static ReceiptDatasetGateway.CustomerCCAuthResponseRow LoadReceiptRecords(string orderNumber, ReceiptFacade facadeReceipt)
        {
            TransactionReceiptDatasetGateway tranRec = new TransactionReceiptDatasetGateway();
            using (TransactionReceiptFacade facadeTranRec = new TransactionReceiptFacade(tranRec))
            {
                ReceiptDatasetGateway gatewayReceiptDataset = (ReceiptDatasetGateway)facadeReceipt.CurrentDataset;
                RemovePaymentTableRelation(gatewayReceiptDataset);

                string[][] parameterSet = new string[][]{
                new String[] {tranRec.CustomerTransactionReceiptView.TableName, 
                    StoredProcedures.READCUSTOMERTRANSACTIONRECEIPT,
                    "@DocumentCode", orderNumber}};

                facadeTranRec.LoadDataSet(parameterSet,
                    Interprise.Framework.Base.Shared.Enum.ClearType.None,
                    Interprise.Framework.Base.Shared.Enum.ConnectionStringType.Online);


                if (tranRec.CustomerTransactionReceiptView.Rows.Count > 0)
                {
                    foreach (TransactionReceiptDatasetGateway.CustomerTransactionReceiptViewRow receiptRow in tranRec.CustomerTransactionReceiptView)
                    {
                        Logger.WriteLine("LoadReceiptRecords: ReceivableCode=" + receiptRow.ReceivableCode);

                        parameterSet = new string[][]{
                        new String[] {gatewayReceiptDataset.CustomerCCAuthResponse.TableName, 
                            StoredProcedures.READCUSTOMERCCAUTHRESPONSE, 
                            "@DocumentCode", receiptRow.ReceivableCode}};

                        facadeReceipt.LoadDataSet(parameterSet, Interprise.Framework.Base.Shared.Enum.ClearType.All,
                            Interprise.Framework.Base.Shared.Enum.ConnectionStringType.Online);

                        foreach (ReceiptDatasetGateway.CustomerCCAuthResponseRow currentAuthResponseRow in gatewayReceiptDataset.CustomerCCAuthResponse)
                        {
                            if (currentAuthResponseRow.ResponseMsg == "Google Checkout")
                            {
                                //Load the matching payment method record.
                                //We need to load the CustomerCardPayment record also, otherwise we can't save changes
                                //to the dataset.
                                parameterSet = new string[][]{
                                new string[] {gatewayReceiptDataset.CustomerPayment.TableName,
                                    StoredProcedures.READCUSTOMERPAYMENT,
                                    "@ReceivableCode", receiptRow.ReceivableCode},
                                new string[] {gatewayReceiptDataset.CustomerCardPayment.TableName,
                                    StoredProcedures.READCUSTOMERCARDPAYMENT,
                                    "@CardPaymentCode", receiptRow.ReceivableCode},
                                new String[] {gatewayReceiptDataset.PaymentMethodView.TableName, 
                                    StoredProcedures.READPAYMENTMETHOD, 
                                    "@ReceivableCode", receiptRow.ReceivableCode}};
                                facadeReceipt.LoadDataSet(parameterSet, Interprise.Framework.Base.Shared.Enum.ClearType.None,
                                    Interprise.Framework.Base.Shared.Enum.ConnectionStringType.Online);

                                //Return the auth response row.
                                return currentAuthResponseRow;
                            }
                        }
                    }
                }
                else
                {
                    Logger.WriteLine("LoadReceiptRecords: Error no CustomerTransactionReceipt records found.");
                    return null;
                }

            }

            return null;
        }

        private static void LoadPaymentMethod(string documentCode, ReceiptFacade facadeReceipt)
        {
            TransactionReceiptDatasetGateway tranRec = new TransactionReceiptDatasetGateway();
            using (TransactionReceiptFacade facadeTranRec = new TransactionReceiptFacade(tranRec))
            {
                ReceiptDatasetGateway gatewayReceiptDataset = (ReceiptDatasetGateway)facadeReceipt.CurrentDataset;
                RemovePaymentTableRelation(gatewayReceiptDataset);

                string[][] parameterSet = new string[][]{
                new String[] {tranRec.CustomerTransactionReceiptView.TableName, 
                    StoredProcedures.READCUSTOMERTRANSACTIONRECEIPT,
                    "@DocumentCode", documentCode}};

                facadeTranRec.LoadDataSet(parameterSet,
                    Interprise.Framework.Base.Shared.Enum.ClearType.None,
                    Interprise.Framework.Base.Shared.Enum.ConnectionStringType.Online);


                if (tranRec.CustomerTransactionReceiptView.Rows.Count > 0)
                {
                    foreach (TransactionReceiptDatasetGateway.CustomerTransactionReceiptViewRow receiptRow in tranRec.CustomerTransactionReceiptView)
                    {
                        Logger.WriteLine("LoadReceiptRecords: ReceivableCode=" + receiptRow.ReceivableCode);

                        //Load the matching payment method record.
                        //We need to load the CustomerCardPayment record also, otherwise we can't save changes
                        //to the dataset.
                        parameterSet = new string[][]{
                        new string[] {gatewayReceiptDataset.CustomerPayment.TableName,
                            StoredProcedures.READCUSTOMERPAYMENT,
                            "@ReceivableCode", receiptRow.ReceivableCode},
                        new string[] {gatewayReceiptDataset.CustomerCardPayment.TableName,
                            StoredProcedures.READCUSTOMERCARDPAYMENT,
                            "@CardPaymentCode", receiptRow.ReceivableCode},
                        new String[] {gatewayReceiptDataset.PaymentMethodView.TableName, 
                            StoredProcedures.READPAYMENTMETHOD, 
                            "@ReceivableCode", receiptRow.ReceivableCode}};
                        facadeReceipt.LoadDataSet(parameterSet, Interprise.Framework.Base.Shared.Enum.ClearType.None,
                            Interprise.Framework.Base.Shared.Enum.ConnectionStringType.Online);
                    }
                }
                else
                {
                    Logger.WriteLine("LoadPaymentMethod: Error no CustomerTransactionReceipt records found.");
                }

            }
        }

        private static void RemovePaymentTableRelation(ReceiptDatasetGateway receiptDataset)
        {
            DataRelation relation = receiptDataset.GetRelation(receiptDataset.CustomerCardPayment, receiptDataset.PaymentMethodView);
            if (relation != null)
            {
                if (receiptDataset.PaymentMethodView.Constraints.Contains(relation.RelationName))
                {
                    receiptDataset.PaymentMethodView.Constraints.Remove(relation.RelationName);
                }
                receiptDataset.Relations.Remove(relation);
            }
            relation = receiptDataset.GetRelation(receiptDataset.CustomerPayment, receiptDataset.PaymentMethodView);
            if (relation != null)
            {
                if (receiptDataset.PaymentMethodView.Constraints.Contains(relation.RelationName))
                {
                    receiptDataset.PaymentMethodView.Constraints.Remove(relation.RelationName);
                }
                receiptDataset.Relations.Remove(relation);
            }
            receiptDataset = null;
        }



        public static void ProcessRiskInformationNotification(string xmlData)
        {
            gc.RiskInformationNotification gRiskInformationNotification = new gc.RiskInformationNotification();
            gRiskInformationNotification = (gc.RiskInformationNotification)DecodeRequest(xmlData, gRiskInformationNotification.GetType());

            try
            {
                // TBD: Provide implementation here...           
                // http://code.google.com/apis/checkout/developer/Google_Checkout_XML_API_Notification_API.html#risk_information_notification

                Order thisOrder = GetOrderByGoogleOrderNumber(gRiskInformationNotification.googleordernumber);

                ReceiptDatasetGateway gatewayReceiptDataset = new ReceiptDatasetGateway();
                using (ReceiptFacade facadeReceipt = new ReceiptFacade(gatewayReceiptDataset))
                {
                    //Indicate that we are dealing with GoogleCheckout.
                    facadeReceipt.IsGoogleCheckOut = true;

                    //Get the customer CC auth row and related tables.
                    ReceiptDatasetGateway.CustomerCCAuthResponseRow authResponseRow =
                        LoadReceiptRecords(thisOrder.OrderNumber, facadeReceipt);

                    if (authResponseRow != null)
                    {
                        //Update the cc auth row with the risk info.
                        authResponseRow.BeginEdit();
                        authResponseRow.AuthCode = gRiskInformationNotification.serialnumber;
                        authResponseRow.AVSResult = gRiskInformationNotification.riskinformation.avsresponse;
                        authResponseRow.CVResult = gRiskInformationNotification.riskinformation.cvnresponse;

                        Logger.WriteLine("authRow.AuthorizationResult = " + authResponseRow.AuthorizationResult);

                        if (authResponseRow.AuthorizationResult == "Authorization Pending")
                            //The Authorization Result field is set to "Authorization Pending" so
                            //add in the eligibleforprotection and buyeraccountage values but leave a place holder for
                            //the expiration date.
                            authResponseRow.AuthorizationResult = string.Format("{0}>{1}>{2}", "{0}", Convert.ToString(gRiskInformationNotification.riskinformation.eligibleforprotection), Convert.ToString(gRiskInformationNotification.riskinformation.buyeraccountage));
                        else
                        {
                            //The Authorization Result field is not set to "Authorization Pending"
                            //which probably means that the authroization notification was proccessed frist.
                            //If this is the case we the value should have the second and third places holders 
                            //that need to be updated with the eligibleforprotection and buyeraccountage values.
                            authResponseRow.AuthorizationResult = string.Format(authResponseRow.AuthorizationResult, Convert.ToString(gRiskInformationNotification.riskinformation.eligibleforprotection), Convert.ToString(gRiskInformationNotification.riskinformation.buyeraccountage));
                        }

                        authResponseRow.EndEdit();

                        //Update the payment method row with the credit card number if the customer indicated to.
                        if (gatewayReceiptDataset.PaymentMethodView.Rows.Count > 0)
                        {
                            gatewayReceiptDataset.PaymentMethodView[0].BeginEdit();
                            gatewayReceiptDataset.PaymentMethodView[0].CreditCard = facadeReceipt.EncryptCardNumber(authResponseRow.CreditCard, gatewayReceiptDataset.PaymentMethodView[0]);
                            gatewayReceiptDataset.PaymentMethodView[0].CreditCardOnFile = authResponseRow.CreditCard;
                            gatewayReceiptDataset.PaymentMethodView[0].EndEdit();
                        }
                        else if(gatewayReceiptDataset.PaymentMethodView.Rows.Count == 0)
                        {
                            Logger.WriteLine("processRiskInformationNotification: Error no PaymentMethodView record found.");
                        }

                        //Commit the PaymentMethod and CC Auth records.
                        string[][] newSet = new string[][] {
                        new string[] { gatewayReceiptDataset.PaymentMethodView.TableName, 
                            Interprise.Framework.Base.Shared.StoredProcedures.CREATEPAYMENTMETHOD, 
                            Interprise.Framework.Base.Shared.StoredProcedures.UPDATEPAYMENTMETHOD, 
                            Interprise.Framework.Base.Shared.StoredProcedures.DELETEPAYMENTMETHOD }, 
                        new string[] { gatewayReceiptDataset.CustomerCCAuthResponse.TableName, 
                            Interprise.Framework.Base.Shared.StoredProcedures.CREATECUSTOMERCCAUTHRESPONSE, 
                            Interprise.Framework.Base.Shared.StoredProcedures.UPDATECUSTOMERCCAUTHRESPONSE, 
                            Interprise.Framework.Base.Shared.StoredProcedures.DELETECUSTOMERCCAUTHRESPONSE } 
                        };

                        if (!facadeReceipt.UpdateDataSet(newSet
                            , Interprise.Framework.Base.Shared.Enum.TransactionType.CustomerReceipt
                            , string.Format("Update Google Checkout Payment with Risk Info. Sales Order: {0}", thisOrder.OrderNumber)
                            , false))
                        {
                            Logger.WriteLine("ProcessRiskInformationNotification, Error updating PaymentMethodView and CustomerCCAuthResponse, Order No.:" + thisOrder.OrderNumber);
                            WriteViolationsToLog("ProcessRiskInformationNotification", gatewayReceiptDataset);
                        }
                        else
                        {
                            Logger.WriteLine("ProcessRiskInformationNotification, Updated PaymentMethodView and CustomerCCAuthResponse, Order No.:" + thisOrder.OrderNumber);
                        }

                    }
                    else
                    {
                        Logger.WriteLine("processRiskInformationNotification: Error no CustomerCCAuthResponse record found.");
                    }
                }
            }
            catch (Exception e)
            {
                // log exception
                Logger.WriteLine("processRiskInformationNotification Exception: " + e.ToString());
            }
        }

        public static string CreateMerchantCalculationResults(string xmlData)
        {
            try
            {
                // API documentation can be found here:
                // http://code.google.com/apis/checkout/developer/Google_Checkout_XML_API_Notification_API.html

                gc.MerchantCalculationCallback gMerchantCalculationCallback = new gc.MerchantCalculationCallback();
                gMerchantCalculationCallback = (gc.MerchantCalculationCallback)DecodeRequest(xmlData, gMerchantCalculationCallback.GetType());


                //Get the shopping cart.
                InterpriseShoppingCart cart = null;
                if (gMerchantCalculationCallback.shoppingcart.merchantprivatedata.Any.Length > 0)
                {
                    //Get the customer guid.
                    XmlNode customerInfo = gMerchantCalculationCallback.shoppingcart.merchantprivatedata.Any[0];
                    Guid customerGuid = new Guid(customerInfo.Attributes["ContactGuid"].Value);
                    Customer thisCustomer = Customer.Find(customerGuid);

                    //Get the shopping cart for this customer.
                    cart = InterpriseShoppingCart.Get(thisCustomer, CartTypeEnum.ShoppingCart, true);
                }
                else
                {
                    Logger.WriteLine("CreateMerchantCalculationResults: ERROR: No Customer Info found.");
                    return null;
                }


                //Make sure we have a cart.
                if (cart == null)
                {
                    Logger.WriteLine("CreateMerchantCalculationResults: ERROR: Unable to load shopping cart.");
                    return null;
                }

                //Instantiate the merchant calculation results object.
                gc.MerchantCalculationResults merchantCalculationResults = new gc.MerchantCalculationResults();
                //Get the customer's currency code.
                string currencyCode = cart.ThisCustomer.CurrencyCode;

                //Create the coupon results list.
                List<gc.CouponResult> couponResults = new List<gc.CouponResult>();

                //Loop through all the coupon codes.
                foreach (gc.MerchantCodeString googleCoupon in gMerchantCalculationCallback.calculate.merchantcodestrings)
                {
                    //Instantiate the results objects.
                    gc.CouponResult couponResult = new gc.CouponResult();
                    gc.CouponResultCalculatedamount calculatedAmount = new gc.CouponResultCalculatedamount();

                    string message = "";
                    //Get the coupon code.
                    couponResult.code = googleCoupon.code;
                    //Set the currency code.
                    calculatedAmount.Currency = currencyCode;

                    //We only support 1 coupon so if we have any additional coupons we need to invalidate them.
                    if (couponResults.Count < 1)
                    {
                        string couponErrorMessage = String.Empty;

                        //Vaidate the coupon code.
                        if (!cart.ThisCustomer.IsRegistered)
                        {
                            //The user is not registered with the merchant so they can't use coupons.
                            //Set the message to display to the user.
                            message = "This merchant requires registration in order to use coupons.";
                            //Indicate that the coupon is invalid and set the value to 0.
                            calculatedAmount.Value = 0;
                            couponResult.valid = false;
                        }
                        else if (String.IsNullOrEmpty(couponResult.code)
                            || !cart.IsCouponValid(cart.ThisCustomer, couponResult.code, ref couponErrorMessage))
                        {
                            //The code is blank or not a valid code in the system.

                            //Set the message to display to the user.
                            message = "Invalid Coupon: " + couponResult.code
                                + (string.IsNullOrEmpty(couponErrorMessage) ? string.Empty : "; " + couponErrorMessage);
                            //Indicate that the coupon is invalid and set the value to 0.
                            calculatedAmount.Value = 0;
                            couponResult.valid = false;
                        }
                        else
                        {

                            //Apply the coupon.
                            cart.ApplyCoupon(couponResult.code);

                            //We passed all validations. Indicate that the coupon is valid and prep the message.
                            //We will calculate the discount amount for each shipping method below.

                            //Set the value and message to display to the user.
                            message = "Congratulations! You saved {0} in your order.";
                            //Indicate that the coupon is valid.
                            couponResult.valid = true;
                        }
                    }
                    else
                    {
                        //Set the message to display to the user. Let them know that we only support one coupon.
                        message = "This merchant only allows one coupon code.";
                        //Indicate that the coupon is invalid and set the value to 0.
                        calculatedAmount.Value = 0;
                        couponResult.valid = false;
                    }

                    //Add in the calculated amount and the messege to the results object.
                    couponResult.calculatedamount = calculatedAmount;
                    couponResult.message = message;

                    //Add the coupon to the collection.
                    couponResults.Add(couponResult);

                    //Log the message.
                    Logger.WriteLine(message);
                }

                //Remove any coupons applied if we have no coupon results.
                if (couponResults.Count == 0)
                {
                    cart.ClearCoupon();
                }

                gc.Result[] merchantResults = new gc.Result[gMerchantCalculationCallback.calculate.shipping.Length];


                string addressID = String.Empty;
                gc.AnonymousAddress googleShipToAddress = null;

                //Get the ship to address.
                if (gMerchantCalculationCallback.calculate.addresses.Length > 0)
                {
                    googleShipToAddress = gMerchantCalculationCallback.calculate.addresses[0];
                }

                //Get the shipping methods for this address (if we have one).
                ShippingMethodDTOCollection shippingMethods = new ShippingMethodDTOCollection();
                if (googleShipToAddress != null)
                {
                    addressID = googleShipToAddress.id;

                    var defaultAddress = new Address() 
                    { 
                        City = googleShipToAddress.city,
                        State = googleShipToAddress.region,
                        PostalCode = googleShipToAddress.postalcode,
                        CountryISOCode = googleShipToAddress.countrycode,
                        AddressID = cart.ThisCustomer.PrimaryShippingAddress.AddressID,
                        ResidenceType = cart.ThisCustomer.PrimaryShippingAddress.ResidenceType
                    };

                    //Get all the shipping methods for this address.
                    shippingMethods = cart.GetShippingMethods(defaultAddress);
                }

                gc.Result lowestCalculationResult = null;

                for (int index = 0; index < gMerchantCalculationCallback.calculate.shipping.Length; index++)
                {
                    string shippingName = gMerchantCalculationCallback.calculate.shipping[index].name;

                    gc.Result calculationResult = new gc.Result();
                    calculationResult.addressid = addressID;

                    gc.ResultShippingrate gResultShippingRate = new gc.ResultShippingrate();

                    //Find the matching shipping method.
                    var shippingMethod = shippingMethods.Find(f => f.Description.Trim() == shippingName.Trim());

                    //If we found the matching shipping method then use it.
                    if (shippingMethod != null)
                    {
                        gResultShippingRate.Value = shippingMethod.Freight;
                        gResultShippingRate.Currency = shippingMethod.FreightCurrencyCode;

                        //See if the coupon includes free shipping.
                        if (cart.CouponIncludesFreeShipping(cart.ThisCustomer.CouponCode))
                        {
                            //Check to see if this is are first time here (lowestCalculationResult == null)
                            //or the lowest result has a greater shipping rate then the current result.
                            if (lowestCalculationResult == null
                                || lowestCalculationResult.shippingrate.Value > gResultShippingRate.Value)
                            {
                                //Disable the current lowest result since it no longer is the lowest.
                                if (lowestCalculationResult != null)
                                {
                                    lowestCalculationResult.shippable = false;
                                }
                                //Replace the lowest result with the current result method
                                //and enable it.
                                lowestCalculationResult = calculationResult;
                                calculationResult.shippable = true;
                            }
                            else
                            {
                                //The current result is not the lowest so disable it.
                                calculationResult.shippable = false;
                            }
                        }
                        else
                        {
                            //The coupon does not include free shipping so enable the result.
                            calculationResult.shippable = true;
                        }
                    }
                    else
                    {
                        //If we don't have a match then we can't ship to it.
                        gResultShippingRate.Currency = currencyCode;
                        gResultShippingRate.Value = Decimal.Zero;
                        calculationResult.shippable = false;
                    }
                    calculationResult.shippingrate = gResultShippingRate;

                    calculationResult.shippingname = shippingName;
                    calculationResult.shippableSpecified = true;


                    //Get the Tax amount.
                    gc.ResultTotaltax gResultTotalTax = new gc.ResultTotaltax();
                    gResultTotalTax.Currency = currencyCode;
                    gResultTotalTax.Value = cart.GetCartTaxTotal();
                    calculationResult.totaltax = gResultTotalTax;

                    //Add it to the collection.
                    merchantResults[index] = calculationResult;
                }

                //See if we had free shipping.
                if (cart.CouponIncludesFreeShipping(cart.ThisCustomer.CouponCode))
                {
                    //See if we had a lowest result (if not then it would mean we had no valid shipping methods).
                    if (lowestCalculationResult != null)
                    {
                        //This will be the only enabled shipping value so make it free.
                        lowestCalculationResult.shippingrate.Value = 0;
                    }
                }

                //Loop through all the results and apply the coupons.
                //The reason we didn't apply the coupons during the creating of the calcuation result loop above
                //is because if we had an item with free shipping we need to zero out it's value first so
                //that we get the correct coupon discount amount.
                foreach (gc.Result currentResult in merchantResults)
                {
                    AddCouponsToCalculationResults(cart, currentResult, couponResults);
                }

                merchantCalculationResults.results = merchantResults;

                return SerializeResponse(merchantCalculationResults);

            }
            catch (Exception ex)
            {
                Logger.WriteLine("CreateMerchantCalculationResults Error: " + ex.ToString());
            }
            return null;
        }

        public static string ProcessNewOrderNotification(string xmlData, HttpContext currentContext, XmlNode customerInfo)
        {
            var gNewOrderNotification = DecodeRequest(xmlData,typeof(gc.NewOrderNotification)) as gc.NewOrderNotification;
            try
            {
                // grab customer ID from Google Order
                var CustomerInfo = gNewOrderNotification.shoppingcart.merchantprivatedata.Any[0];
                if(customerInfo != null) 
                {
                    CustomerInfo = customerInfo;
                }
                
                string customerCode = CustomerInfo.Attributes["CustomerCode"].Value;
                string contactCode = CustomerInfo.Attributes["ContactCode"].Value;
                var customerGuid = new Guid(CustomerInfo.Attributes["ContactGuid"].Value);

                var thisCustomer = Customer.Find(customerGuid);
                InterpriseHelper.UpdateCustomerPaymentTerm(thisCustomer, AppLogic.AppConfig("GoogleCheckout.PaymentTerm"));

                // billing
                bool hasMatchingShipToAddress = false;
                string shipToCode = string.Empty;
                string twoletterShipToCountryISOCode = gNewOrderNotification.buyershippingaddress.countrycode.Trim();
                string shipToCountry = GetCountryCodeFromTwoLetterISOCode(twoletterShipToCountryISOCode);

                var query = new StringBuilder();
                query.AppendFormat("SELECT ShipToCode FROM CustomerShipTo WITH (NOLOCK) WHERE CustomerCode = {0}", DB.SQuote(customerCode));
                if(gNewOrderNotification.buyershippingaddress.contactname.Trim() == "")
                {
                    query.Append(" AND (ShipToName = '' OR ShipToName IS NULL)");
                }
                else
                {
                    query.AppendFormat(" AND ShipToName = {0}", DB.SQuote(gNewOrderNotification.buyershippingaddress.contactname.Trim()));
                }
                if (gNewOrderNotification.buyershippingaddress.address1.Trim() == "")
                {
                    query.Append(" AND (Address = '' OR Address IS NULL)");
                }
                else
                {
                    query.AppendFormat(" AND Address = {0}", DB.SQuote(gNewOrderNotification.buyershippingaddress.address1.Trim()));
                }
                if (gNewOrderNotification.buyershippingaddress.city.Trim() == "")
                {
                    query.Append(" AND (City = '' OR City IS NULL)");
                }
                else
                {
                    query.AppendFormat(" AND City = {0}", DB.SQuote(gNewOrderNotification.buyershippingaddress.city.Trim()));
                }
                if (gNewOrderNotification.buyershippingaddress.region.Trim() == "")
                {
                    query.Append(" AND (State = '' OR State IS NULL)");
                }
                else
                {
                    query.AppendFormat(" AND State = {0}", DB.SQuote(gNewOrderNotification.buyershippingaddress.region.Trim()));
                }
                if (gNewOrderNotification.buyershippingaddress.postalcode.Trim() == "")
                {
                    query.Append(" AND (PostalCode = '' OR PostalCode IS NULL)");
                }
                else
                {
                    query.AppendFormat(" AND PostalCode = {0}", DB.SQuote(gNewOrderNotification.buyershippingaddress.postalcode.Trim()));
                }

                using (var con = DB.NewSqlConnection())
                {
                    con.Open();
                    using (var reader = DB.GetRSFormat(con, query.ToString()))
                    {
                        if (reader.Read())
                        {
                            hasMatchingShipToAddress = true;
                            shipToCode = DB.RSField(reader, "ShipToCode");
                        }
                    }
                }

                Address shippingAddress = null;
                if (hasMatchingShipToAddress == false)
                {
                    shippingAddress = new Address()
                    {
                        ThisCustomer = thisCustomer,
                        CustomerCode = thisCustomer.CustomerCode,
                        AddressType = AddressTypes.Shipping,
                        Name = gNewOrderNotification.buyershippingaddress.contactname.Trim(),
                        Address1 = gNewOrderNotification.buyershippingaddress.address1.Trim(),
                        City = gNewOrderNotification.buyershippingaddress.city.Trim(),
                        State = gNewOrderNotification.buyershippingaddress.region.Trim(),
                        PostalCode = gNewOrderNotification.buyershippingaddress.postalcode.Trim(),
                        Phone = gNewOrderNotification.buyershippingaddress.phone.Trim(),
                        Country = shipToCountry,
                        ResidenceType = ResidenceTypes.Residential,
                        EMail = gNewOrderNotification.buyershippingaddress.email.Trim()
                    };

                    //Only add the shipto to the customer if they are registered.
                    if (thisCustomer.IsRegistered)
                    {
                        shipToCode = InterpriseHelper.AddCustomerShipTo(shippingAddress, thisCustomer);
                    }
                    else
                    {
                        shippingAddress.Save();
                    }
                }
                else
                {
                    shippingAddress = new Address();
                    shippingAddress.LoadByCustomer(thisCustomer, AddressTypes.Shipping, shipToCode);
                }

                if (thisCustomer.IsRegistered)
                {
                    // update the shoppingcart line items to point to the new billing address
                    DB.ExecuteSQL("update EcommerceShoppingCart set ShippingAddressID = {0} where CustomerCode = {1} and ContactCode = {2} and CartType = {3}",
                                  DB.SQuote(shipToCode), DB.SQuote(customerCode), DB.SQuote(contactCode), ((int)CartTypeEnum.ShoppingCart).ToString());

                    // set as the default...
                    DB.ExecuteSQL("UPDATE CRMContact SET DefaultShippingCode = {0} WHERE ContactCode = {1}", DB.SQuote(shipToCode), DB.SQuote(contactCode));
                }

                // shipping
                bool hasMatchingBillToToAddress = false;
                string billToCode = string.Empty;

                string twoletterBillToCountryISOCode = gNewOrderNotification.buyerbillingaddress.countrycode.Trim();
                string billToCountry = GetCountryCodeFromTwoLetterISOCode(twoletterBillToCountryISOCode);

                query = new StringBuilder();
                query.AppendFormat("SELECT CreditCardCode FROM CustomerCreditCard WITH (NOLOCK) WHERE CustomerCode = {0}", DB.SQuote(customerCode));
                if (gNewOrderNotification.buyerbillingaddress.contactname.Trim() == "")
                {
                    query.Append(" AND (NameOnCard = '' OR NameOnCard IS NULL)");
                }
                else
                {
                    query.AppendFormat(" AND NameOnCard = {0}", DB.SQuote(gNewOrderNotification.buyerbillingaddress.contactname.Trim()));
                }
                if (gNewOrderNotification.buyerbillingaddress.address1.Trim() == "")
                {
                    query.Append(" AND (Address = '' OR Address IS NULL)");
                }
                else
                {
                    query.AppendFormat(" AND Address = {0}", DB.SQuote(gNewOrderNotification.buyerbillingaddress.address1.Trim()));
                }
                if (gNewOrderNotification.buyerbillingaddress.city.Trim() == "")
                {
                    query.Append(" AND (City = '' OR City IS NULL)");
                }
                else
                {
                    query.AppendFormat(" AND City = {0}", DB.SQuote(gNewOrderNotification.buyerbillingaddress.city.Trim()));
                }
                if (gNewOrderNotification.buyerbillingaddress.region.Trim() == "")
                {
                    query.Append(" AND (State = '' OR State IS NULL)");
                }
                else
                {
                    query.AppendFormat(" AND State = {0}", DB.SQuote(gNewOrderNotification.buyerbillingaddress.region.Trim()));
                }
                if (gNewOrderNotification.buyerbillingaddress.postalcode.Trim() == "")
                {
                    query.Append(" AND (PostalCode = '' OR PostalCode IS NULL)");
                }
                else
                {
                    query.AppendFormat(" AND PostalCode = {0}", DB.SQuote(gNewOrderNotification.buyerbillingaddress.postalcode.Trim()));
                }

                using (var con = DB.NewSqlConnection())
                {
                    con.Open();
                    using (var reader = DB.GetRSFormat(con, query.ToString()))
                    {
                        if (reader.Read())
                        {
                            hasMatchingBillToToAddress = true;
                            billToCode = DB.RSField(reader, "CreditCardCode");
                        }
                    }
                }

                Address billingAddress = null;

                if (hasMatchingBillToToAddress)
                {
                    billingAddress = new Address();
                    billingAddress.LoadByCustomer(thisCustomer, AddressTypes.Billing, billToCode);
                }
                else
                {
                    billingAddress = new Address();
                    billingAddress.ThisCustomer = thisCustomer;
                    billingAddress.CustomerCode = thisCustomer.CustomerCode;
                    billingAddress.AddressType = AddressTypes.Billing;

                    billingAddress.Name = gNewOrderNotification.buyerbillingaddress.contactname.Trim();
                    billingAddress.Address1 = gNewOrderNotification.buyerbillingaddress.address1.Trim();
                    billingAddress.City = gNewOrderNotification.buyerbillingaddress.city.Trim();
                    billingAddress.State = gNewOrderNotification.buyerbillingaddress.region.Trim();
                    billingAddress.PostalCode = gNewOrderNotification.buyerbillingaddress.postalcode.Trim();
                    billingAddress.Phone = gNewOrderNotification.buyerbillingaddress.phone.Trim();
                    billingAddress.Country = billToCountry;
                    billingAddress.ResidenceType = ResidenceTypes.Residential;
                    billingAddress.EMail = gNewOrderNotification.buyerbillingaddress.email.Trim();

                    //Only add the billto to the customer if they are registered.
                    if (thisCustomer.IsRegistered)
                    {
                        InterpriseHelper.AddCustomerBillToInfo(billingAddress.CustomerCode, billingAddress, true, thisCustomer.EMail);
                        billingAddress = thisCustomer.PrimaryBillingAddress;
                        billToCode = billingAddress.AddressID;
                    }
                    else
                    {
                        billingAddress.Save();
                    }
                }

                string pagename = CommonLogic.GetCurrentPageName(currentContext);
                // load the shoppingCart - Overload for thread safe. Pagename
                var cart = new InterpriseShoppingCart(null,
                                                    thisCustomer.SkinID,
                                                    thisCustomer,
                                                    CartTypeEnum.ShoppingCart,
                                                    String.Empty,
                                                    false, true, pagename);

                //int orderCounter = -1;
                string salesOrderCode = string.Empty;

                if (cart.CartItems.Count > 0)
                {
                    //Clear any existing coupons.
                    cart.ClearCoupon();

                    if (gNewOrderNotification.orderadjustment.merchantcodes.items != null)
                    {
                        //Find the first coupon if one exists and apply it to the cart.
                        foreach (var merchantCodes in gNewOrderNotification.orderadjustment.merchantcodes.items)
                        {
                            if (merchantCodes is gc.CouponAdjustment)
                            {
                                //Cast the object.
                                var gcCoupon = merchantCodes as gc.CouponAdjustment;
                                //Apply the coupon and break out of the loop.
                                cart.ApplyCoupon(gcCoupon.code);
                                break;
                            }
                        }
                    }

                    //Build the sales order details.
                    cart.BuildSalesOrderDetails();

                    // update the notes for the google order number
                    string googleOrderNumber = string.Format("Google Order Number: {0}", gNewOrderNotification.googleordernumber);
                    cart.SalesOrderDataset.CustomerSalesOrderView[0].Notes = googleOrderNumber + Environment.NewLine + cart.SalesOrderDataset.CustomerSalesOrderView[0].Notes;

                    //See if we have shipping.
                    if (!cart.HasShippableComponents() || cart.CouponIncludesFreeShipping(cart.ThisCustomer.CouponCode))
                    {
                        cart.MakeShippingNotRequired();
                    }
                    else if (gNewOrderNotification.orderadjustment.shipping != null)
                    {
                        if (gNewOrderNotification.orderadjustment.shipping.item != null) //Causes object reference error when validating item if shipping is null.
                        {
                            string shippingCode = null;

                            //Get the shipping code from the google new order notification.
                            var shippingAdjustment = gNewOrderNotification.orderadjustment.shipping.item;

                            //Cast the adustment object to the correct object type.
                            if (shippingAdjustment is gc.FlatRateShippingAdjustment)
                            {
                                gc.FlatRateShippingAdjustment flateRate = (gc.FlatRateShippingAdjustment)shippingAdjustment;
                                shippingCode = flateRate.shippingname;
                            }
                            else if (shippingAdjustment is gc.MerchantCalculatedShippingAdjustment)
                            {
                                gc.MerchantCalculatedShippingAdjustment flateRate = (gc.MerchantCalculatedShippingAdjustment)shippingAdjustment;
                                shippingCode = flateRate.shippingname;
                            }
                            else if (shippingAdjustment is gc.CarrierCalculatedShippingAdjustment)
                            {
                                gc.CarrierCalculatedShippingAdjustment flateRate = (gc.CarrierCalculatedShippingAdjustment)shippingAdjustment;
                                shippingCode = flateRate.shippingname;
                            }
                            else if (shippingAdjustment is gc.PickupShippingAdjustment)
                            {
                                gc.PickupShippingAdjustment flateRate = (gc.PickupShippingAdjustment)shippingAdjustment;
                                shippingCode = flateRate.shippingname;
                            }

                            //Find the matching shipping method with the code we got from google.
                            var availableShippingMethods = cart.GetShippingMethods(shippingAddress);
                            var shippingMethod = availableShippingMethods.Find(s => s.Code.Trim() == shippingCode.Trim());
                            if (shippingMethod != null)
                            {
                                //Add in the shipping method.
                                cart.SetCartShippingMethod(shippingMethod.Code, string.Empty);
                            }
                        }
                    }

                    //Place the order.
                    string receiptCode = string.Empty;
                    string status = cart.PlaceOrder(null, billingAddress, shippingAddress, ref salesOrderCode, ref receiptCode, true, true, null, true, false);

                    if (status != AppLogic.ro_OK)
                    {
                        Logger.WriteLine("processNewOrderNotification Problem: " + status);
                    }
                    else
                    {
                        thisCustomer.ThisCustomerSession["OrderNumber"] = salesOrderCode;
                        AddInitalPayment(salesOrderCode, thisCustomer, gNewOrderNotification);
                    }
                }
                else
                {
                    // cart is empty already...
                    // log error, can't continue
                    Logger.WriteLine("processNewOrderNotification Problem: Cart is empty already.");
                }

                return salesOrderCode;
            }
            catch (Exception e)
            {
                // log exception
                Logger.WriteLine("processNewOrderNotification Exception: " + e.ToString());

                return string.Empty;
            }
        }

        public static bool VerifyMessageAuthentication(string authStr)
        {
            if (authStr == null || authStr.Length == 0 || authStr.IndexOf("Basic", 0) != 0)
            {
                return false;
            }

            byte[] decodedBytes = Convert.FromBase64String(authStr.Trim().Substring(6));
            string decodedAuthString = Encoding.ASCII.GetString(decodedBytes);

            string username = decodedAuthString.Split(':')[0];
            string password = decodedAuthString.Split(':')[1];

            string merchantID;
            string merchantKey;

            if (AppLogic.AppConfigBool("GoogleCheckout.UseSandbox"))
            {
                merchantID = AppLogic.AppConfig("GoogleCheckout.SandboxMerchantId");
                merchantKey = AppLogic.AppConfig("GoogleCheckout.SandboxMerchantKey");
            }
            else
            {
                merchantID = AppLogic.AppConfig("GoogleCheckout.MerchantId");
                merchantKey = AppLogic.AppConfig("GoogleCheckout.MerchantKey");
            }

            return (username == merchantID && password == merchantKey);
        }

        public static string SendNotificationAcknowledgment()
        {
            // Respond to the notification with <notification-acknowledgment>
            var gNotificationAcknowledgment = new gc.NotificationAcknowledgment();
            return SerializeResponse(gNotificationAcknowledgment);
        }

        private static string SerializeResponse(Object oResponse)
        {
            // serialize the document to a string
            StringBuilder strResponse = new StringBuilder();
            StringWriter swResponse = new StringWriter(strResponse);
            XmlSerializer serResponse = new XmlSerializer(oResponse.GetType(), "http://checkout.google.com/schema/2");

            serResponse.Serialize(swResponse, oResponse);
            swResponse.Close();

            // Log the outgoing message
            Logger.WriteLine(strResponse.ToString());

            return strResponse.ToString();
        }

        public static bool IsSupported()
        {
            //added for free and full edition support
            if (InterpriseHelper.ConfigInstance.LicenseInfo.ProductEdition.ToUpper() != Interprise.Licensing.Base.Shared.Enum.ProductEdition.ISB.ToString())
                return true;
            else
                return false;            
        }

        public static string CreateGoogleCheckout(InterpriseShoppingCart cart)
        {
            if (!IsSupported()) return string.Empty;
            var ThisCustomer = cart.ThisCustomer;

            var threadId = ThisCustomer.ContactGUID;
            if (!GcThreadProcessor.Exists(threadId)) { GcThreadProcessor.Add(threadId); }

            // specify merchant calculations url & coupon acceptance
            var gMerchantCalculations = new gc.MerchantCalculations();
            gMerchantCalculations.merchantcalculationsurl = AppLogic.GetStoreHTTPLocation(AppLogic.AppConfigBool("UseSSL")) + 
                                                            "gcCallback.aspx?{0}={1}".FormatWith(DomainConstants.GcQueryParamId, threadId.ToString());
            gMerchantCalculations.acceptmerchantcoupons = true;
            gMerchantCalculations.acceptmerchantcouponsSpecified = true;

            // Add items to the Google Checkout ShoppingCart.
            var collItems = new gc.Item[cart.CartItems.Count];
            for (int ctr = 0; ctr < cart.CartItems.Count; ctr++)
            {
                var cItem = cart.CartItems[ctr];
                var item = new gc.Item();
                item.itemname = HttpUtility.HtmlEncode(cItem.ItemName);
                item.itemdescription = cItem.ItemDescription;

                if (cItem.m_Notes.Length != 0)
                {
                    item.itemdescription += "  " +
                                            AppLogic.GetString("shoppingcart.cs.23", cart.SkinID, cart.ThisCustomer.LocaleSetting) +
                                            HttpUtility.HtmlEncode(cItem.m_Notes);
                }

                const int DESC_LEN = 60;
                const string DESC_TRAIL = "...";

                string desc = item.itemdescription;
                if (desc.Length > DESC_LEN)
                {
                    desc = AppLogic.StripHtml(desc);
                    desc = desc.Replace("\r\n", " ");
                    desc = desc.Replace('\n', ' ');
                    desc = desc.Replace('\r', ' ');

                    int y = desc.LastIndexOf(' ', DESC_LEN - DESC_TRAIL.Length, DESC_LEN - DESC_TRAIL.Length);
                    if (y <= 0) y = DESC_LEN;

                    item.itemdescription = desc.Substring(0, y) + DESC_TRAIL;
                }

                //Check if quantity is not decimal. Google Checkout only supports int value.
                if (!IsQuantityValid(cItem.m_Quantity.ToString()))
                {
                    return string.Format("shoppingcart.aspx?errormsg={0}", AppLogic.GetString("shoppingcart.cs.44", ThisCustomer.SkinID, ThisCustomer.LocaleSetting));
                }

                item.quantity = Convert.ToInt32(cItem.m_Quantity); //Google does not support fractional quantity.
                item.unitprice.Currency = Localization.StoreCurrency();
                item.unitprice.Value = cItem.UnitPrice;

                //The item is a digital download so we need to let Google know. This way Google will not charge shipping.
                if (cItem.IsDownload)
                {
                    item.digitalcontent = new gc.DigitalContent();
                    item.digitalcontent.emaildelivery = true;
                    item.digitalcontent.emaildeliverySpecified = true;
                    item.digitalcontent.displaydisposition = "PESSIMISTIC";
                }

                collItems[ctr] = item;
            }

            var gShippingMethods = new gc.MerchantCheckoutFlowSupportShippingmethods();
            string domesticCountryCode = AppLogic.AppConfig("GoogleCheckout.DefaultDomesticShipToCountry");

            // Using Merchant Calculated Shipping
            // Get list of shipping methods for domestic and international
            var aa = new gc.AnonymousAddress()
            {
                city = AppLogic.AppConfig("GoogleCheckout.DefaultDomesticShipToCity"),
                region = AppLogic.AppConfig("GoogleCheckout.DefaultDomesticShipToState"),
                postalcode = AppLogic.AppConfig("GoogleCheckout.DefaultDomesticShipToZip"),
                countrycode = domesticCountryCode
            };

            // allow the world area (this will be filtered upon callback)
            var gDomWA = new gc.WorldArea[1];
            gDomWA[0] = new gc.WorldArea();
            var gDomAA = new gc.ShippingRestrictionsAllowedareas();
            gDomAA.items = gDomWA;

            var defaultAddress = new Address() 
            { 
                City = aa.city,
                State = aa.region,
                PostalCode = aa.postalcode,
                CountryISOCode = aa.countrycode,
                AddressID = ThisCustomer.PrimaryShippingAddress.AddressID,
                ResidenceType = ThisCustomer.PrimaryShippingAddress.ResidenceType
            };

            var shippingMethods = cart.GetShippingMethods(defaultAddress);
            var gShippingMethod = new gc.MerchantCalculatedShipping[shippingMethods.Count];
            for (int ctr = 0; ctr < shippingMethods.Count; ctr++)
            {
                var shippingMethod = shippingMethods[ctr];
                gShippingMethod[ctr] = new gc.MerchantCalculatedShipping();
                gShippingMethod[ctr].name = shippingMethod.Description;

                gShippingMethod[ctr].price = new gc.MerchantCalculatedShippingPrice();
                gShippingMethod[ctr].price.Value = shippingMethod.Freight * AppLogic.AppConfigNativeDecimal("GoogleCheckout.DefaultShippingMarkup");
                gShippingMethod[ctr].price.Currency = Localization.StoreCurrency();

                // allow the world area (this will be filtered upon callback)
                gShippingMethod[ctr].addressfilters = new gc.ShippingRestrictions();
                gShippingMethod[ctr].addressfilters.allowedareas = gDomAA;

                gShippingMethod[ctr].addressfilters.allowuspobox = !AppLogic.AppConfigBool("DisallowShippingToPOBoxes");
                gShippingMethod[ctr].addressfilters.allowuspoboxSpecified = true;
            }

            gShippingMethods.items = gShippingMethod;

            // specify default zero-tax-rate (overridden upon Merchant callback)
            var gDefaultTaxRule = new gc.DefaultTaxRule()
            {
                rate = AppLogic.AppConfigUSDouble("GoogleCheckout.DefaultTaxRate") / 100,
                taxarea = new gc.DefaultTaxRuleTaxarea(),
                shippingtaxed = AppLogic.AppConfigBool("GoogleCheckout.ShippingIsTaxed"),
                shippingtaxedSpecified = true
            };

            gDefaultTaxRule.taxarea.item = new gc.WorldArea();

            var gDefaultTaxTable = new gc.DefaultTaxTable()
            {
                taxrules = new gc.DefaultTaxRule[1]
            };

            gDefaultTaxTable.taxrules[0] = gDefaultTaxRule;

            // assemble tax rules into tax table, specify merchant-calculated
            var gTaxTables = new gc.TaxTables();
            gTaxTables.defaulttaxtable = gDefaultTaxTable;
            gTaxTables.merchantcalculated = true;
            gTaxTables.merchantcalculatedSpecified = true;

            // set-up url's, and assemble tax tables and shipping methods into a CFS
            var MerchantCFS = new gc.MerchantCheckoutFlowSupport()
            {
                continueshoppingurl = AppLogic.GetStoreHTTPLocation(false) +
                                                    "googlecheckout_postsale.aspx?{0}={1}"
                                                    .FormatWith(DomainConstants.GcQueryParamId, threadId),
                editcarturl = AppLogic.GetStoreHTTPLocation(AppLogic.AppConfigBool("UseSSL")) + "shoppingcart.aspx",
                taxtables = gTaxTables,
                shippingmethods = gShippingMethods,
                merchantcalculations = gMerchantCalculations,
                requestbuyerphonenumber = true,
                requestbuyerphonenumberSpecified = true

            };

            MerchantCFS.roundingpolicy.mode = gc.RoundingMode.HALF_UP;
            MerchantCFS.roundingpolicy.rule = gc.RoundingRule.PER_LINE;

            // paramaterized urls for third-pary conversion tracking
            if (AppLogic.AppConfig("GoogleCheckout.ConversionURL").Length != 0)
            {
                gc.ParameterizedUrl gParamURL = new gc.ParameterizedUrl();
                gParamURL.url = AppLogic.AppConfig("GoogleCheckout.ConversionURL");

                if (AppLogic.AppConfig("GoogleCheckout.ConversionParameters").Length != 0)
                {
                    var collParam = AppLogic.AppConfig("GoogleCheckout.ConversionParameters").Trim().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    var gParams = new gc.UrlParameter[collParam.Length];
                    int cntParams = 0;
                    foreach (string param in collParam)
                    {
                        string[] paramTypeName = param.Trim().Split(new char[] { ' ' }, 2, StringSplitOptions.RemoveEmptyEntries);
                        if (paramTypeName.Length == 2)
                        {
                            var gParam = new gc.UrlParameter();
                            gParam.type = paramTypeName[0];
                            gParam.name = HttpContext.Current.Server.UrlEncode(paramTypeName[1]);
                            gParams[cntParams] = gParam;
                            cntParams++;
                        }
                    }
                    if (cntParams != 0)
                    {
                        gParamURL.parameters = gParams;
                    }
                }

                var gParamURLs = new gc.ParameterizedUrl[1];
                gParamURLs[0] = gParamURL;
                MerchantCFS.parameterizedurls = gParamURLs;
            }

            // Wrap Merchant CFS in a Shopping Cart CFS
            var CFS = new gc.CheckoutShoppingCartCheckoutflowsupport();
            CFS.item = MerchantCFS;

            // Assemble CheckoutShoppingCart
            var gCheckoutShoppingCart = new gc.CheckoutShoppingCart();

            gCheckoutShoppingCart.shoppingcart.items = collItems;
            gCheckoutShoppingCart.shoppingcart.cartexpiration.gooduntildate = DateTime.UtcNow.AddMinutes(15.0);

            //Set the flag so that we get the inital authorization details.
            gCheckoutShoppingCart.orderprocessingsupport = new gc.OrderProcessingSupport();
            gCheckoutShoppingCart.orderprocessingsupport.requestinitialauthdetailsSpecified = true;
            gCheckoutShoppingCart.orderprocessingsupport.requestinitialauthdetails = true;

            var CustomerInfoDoc = new XmlDocument();
            var CustomerInfo = CustomerInfoDoc.CreateElement("CustomerInfo");
            CustomerInfo.SetAttribute("CustomerCode", cart.ThisCustomer.CustomerCode);
            CustomerInfo.SetAttribute("ContactGuid", cart.ThisCustomer.ContactGUID.ToString());
            CustomerInfo.SetAttribute("ContactCode", cart.ThisCustomer.ContactCode.ToString());

            var collPrivate = new XmlNode[1];
            collPrivate[0] = CustomerInfo;
            var anyCustInfo = new gc.anyMultiple();
            anyCustInfo.Any = collPrivate;

            gCheckoutShoppingCart.shoppingcart.merchantprivatedata = anyCustInfo;
            gCheckoutShoppingCart.checkoutflowsupport = CFS;

            string xmlData = SendRequest(gCheckoutShoppingCart);
            if (xmlData != string.Empty)
            {
                // Redirect the customer to the Google Checkout page
                var gCheckoutRedirect = new gc.CheckoutRedirect();
                gCheckoutRedirect = DecodeRequest(xmlData, gCheckoutRedirect.GetType()) as gc.CheckoutRedirect;
                return gCheckoutRedirect.redirecturl;
            }
            else
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// Check if value can be converted from Decimal to Integer.
        /// </summary>
        /// <param name="quantity">Quantity to be check.</param>
        /// <returns>Returns true if value can be converted to int. False if not.</returns>
        private static bool IsQuantityValid(string quantity)
        {
            bool result = false;

            result = System.Text.RegularExpressions.Regex.IsMatch(quantity, @"^\d*([\.][0]{1,6})?$");
            return result;
        }

        public static Object DecodeRequest(string xmlData, System.Type oType)
        {
            // write request to debug log
            Logger.WriteLine("Request Received: " + xmlData);

            // de-serialize a string to a document
            XmlSerializer serRequest = new XmlSerializer(oType, "http://checkout.google.com/schema/2");
            Object oRequest;
            using (StringReader srdRequest = new StringReader(xmlData))
            {
                oRequest = serRequest.Deserialize(srdRequest);
                srdRequest.Close();
            }
            return oRequest;
        }

        /// <summary>
        /// Deserializes the object argument and send the document to GC.
        /// </summary>
        /// <param name="oRequest">The object to deserialize</param>
        /// <param name="PostType">One of the following values: request|checkout, chcekout is for cart posting and all others use request</param>
        /// <returns></returns>
        private static string SendRequest(object oRequest)
        {
            // serialize the document to a string
            StringBuilder strRequest = new StringBuilder();
            StringWriter swRequest = new StringWriter(strRequest);
            XmlSerializer serRequest = new XmlSerializer(oRequest.GetType(), "http://checkout.google.com/schema/2");

            serRequest.Serialize(swRequest, oRequest);
            swRequest.Close();

            string merchantID;
            string merchantKey;

            if (AppLogic.AppConfigBool("GoogleCheckout.UseSandbox"))
            {
                merchantID = AppLogic.AppConfig("GoogleCheckout.SandboxMerchantId");
                merchantKey = AppLogic.AppConfig("GoogleCheckout.SandboxMerchantKey");
            }
            else
            {
                merchantID = AppLogic.AppConfig("GoogleCheckout.MerchantId");
                merchantKey = AppLogic.AppConfig("GoogleCheckout.MerchantKey");
            }


            string url = String.Format(AppLogic.AppConfig("GoogleCheckout.BaseUrl"), merchantID);
            if (AppLogic.AppConfigBool("GoogleCheckout.UseSandbox"))
            {
                url = String.Format(AppLogic.AppConfig("GoogleCheckout.SandBoxCheckoutURL"), merchantID);
            }

            if (AppLogic.AppConfigBool("GoogleCheckout.DiagnosticsOnly"))
            {
                url = url.Replace("/request", "/request/diagnose");
            }

            // Set-up the HttpWebRequest object
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
            webRequest.Method = "POST";

            byte[] str = Encoding.ASCII.GetBytes(merchantID + ":" + merchantKey);
            webRequest.ProtocolVersion = HttpVersion.Version10;
            webRequest.Headers.Add("Authorization: Basic " + Convert.ToBase64String(str));
            webRequest.ContentType = "application/xml; charset=UTF-8";
            webRequest.Accept = "application/xml; charset=UTF-8";

            string strResponse = String.Empty;

            // Transmit the request
            try
            {
                byte[] data = Encoding.UTF8.GetBytes(strRequest.ToString());
                webRequest.ContentLength = data.Length;
                Stream requestStream = webRequest.GetRequestStream();
                requestStream.Write(data, 0, data.Length);
                requestStream.Close();
                Logger.WriteLine("sendRequest: " + strRequest.ToString());

                // get the response
                WebResponse webResponse = null;

                webResponse = webRequest.GetResponse();
                using (StreamReader sr = new StreamReader(webResponse.GetResponseStream()))
                {
                    strResponse = sr.ReadToEnd();
                    sr.Close();
                }
                webResponse.Close();

                // Log the HTTP response
                Logger.WriteLine("Response received: " + strResponse);

                //check for diagnostic response and remove it (hopefully it was logged above)
                if (strResponse.Contains("<diagnosis"))
                {
                    strResponse = String.Empty;
                }

                webRequest = null;
                requestStream = null;
                webResponse = null;
            }
            catch (Exception e)
            {
                Logger.WriteLine("sendrequest Exception: " + e.ToString() + " Request: " + strRequest.ToString());
            }

            return strResponse;
        }

        public static void ProcessErrorNotification(string xmlData)
        {
            gc.ErrorResponse gErrorResponse = new gc.ErrorResponse();
            gErrorResponse = (gc.ErrorResponse)DecodeRequest(xmlData, gErrorResponse.GetType());

            Logger.WriteLine("Error response message received:");
            Logger.WriteLine("Error: " + gErrorResponse.errormessage);
            foreach (string Warning in gErrorResponse.warningmessages)
            {
                Logger.WriteLine("Warning: " + Warning);
            }
        }

        public static void ProcessRequestReceived(string xmlData)
        {
            // nothing required
        }

        public static void ProcessChargebackAmountNotification(string XmlData)
        {
            gc.ChargebackAmountNotification gChargebackAmountNotification = new gc.ChargebackAmountNotification();

            // TBD: Provide implementation here...     
            // http://code.google.com/apis/checkout/developer/Google_Checkout_XML_API_Notification_API.html
        }

        public static string GetGoogleOrderNumber(string OrderCode)
        {
            string googleOrderNumber = String.Empty;
            string strOrderQuery = string.Concat("exec eCommercegetOrder @OrderNumber = ", DB.SQuote(OrderCode));
            Order thisOrder = new Order(OrderCode);

            try
            {
                using (SqlConnection con = DB.NewSqlConnection())
                {
                    con.Open();
                    using (IDataReader orderReader = DB.GetRSFormat(con, strOrderQuery))
                    {
                        if (orderReader.Read())
                        {
                            string strNotes = orderReader["CustomerServiceNotes"].ToString();
                            int intStartIndex = strNotes.LastIndexOf("Google Order Number: ") + 21;
                            int intEndIndex = strNotes.IndexOf(Environment.NewLine, intStartIndex);
                            googleOrderNumber = strNotes.Substring(intStartIndex, intEndIndex - intStartIndex);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLine("getGoogleOrderNumber Error: " + ex.ToString() + "googleOrderNumber: '" + googleOrderNumber + "'");
            }
            return googleOrderNumber;
        }

        public static void ProcessDiagnosisNotification(string xmlData)
        {
            gc.DiagnoseResponse gDiagnoseResponse = new gc.DiagnoseResponse();
            gDiagnoseResponse = (gc.DiagnoseResponse)DecodeRequest(xmlData, gDiagnoseResponse.GetType());

            Logger.WriteLine("Diagnosis response message received:");
            Logger.WriteLine("InputXml: " + gDiagnoseResponse.inputxml.InnerXml);
            foreach (string Warning in gDiagnoseResponse.warnings)
            {
                Logger.WriteLine("Warning: " + Warning);
            }
        }

        private static gc.CouponResult CopyCouponResult(gc.CouponResult sourceCouponResult)
        {
            gc.CouponResult newCouponResult = new gc.CouponResult();

            newCouponResult.code = sourceCouponResult.code;
            newCouponResult.message = sourceCouponResult.message;
            newCouponResult.valid = sourceCouponResult.valid;
            newCouponResult.calculatedamount = new gc.CouponResultCalculatedamount();
            newCouponResult.calculatedamount.Currency = sourceCouponResult.calculatedamount.Currency;
            newCouponResult.calculatedamount.Value = sourceCouponResult.calculatedamount.Value;

            return newCouponResult;
        }

        private static void AddCouponsToCalculationResults(InterpriseShoppingCart cart, gc.Result calculationResult, List<gc.CouponResult> couponResults)
        {
            //If we have coupons then add them.
            if (couponResults.Count > 0)
            {
                //Add the coupons to the results.
                calculationResult.merchantcoderesults = new gc.ResultMerchantcoderesults();
                calculationResult.merchantcoderesults.Items = couponResults.ToArray();

                //Now we need to get the coupon applied amount.
                //We only support one coupon so just grab the first one.
                gc.CouponResult couponResult = couponResults[0];
                //Make sure the coupon is valid.
                if (couponResult.valid)
                {
                    //Get a copy of the coupon.
                    gc.CouponResult newCouponResult = CopyCouponResult(couponResult);

                    //Get the discount amount.
                    newCouponResult.calculatedamount.Value = InterpriseHelper.GetCouponDiscountApplied(cart, calculationResult.totaltax.Value, calculationResult.shippingrate.Value);

                    //Update the message.
                    decimal formattedValue = System.Convert.ToDecimal(InterpriseSuiteEcommerceCommon.InterpriseHelper.FormatCurrencyForCustomer(newCouponResult.calculatedamount.Value, calculationResult.shippingrate.Currency).Remove(0, 1));
                    newCouponResult.message = string.Format(newCouponResult.message, formattedValue);

                    //Replace the coupon with the updated copy.
                    calculationResult.merchantcoderesults.Items[0] = newCouponResult;
                }
            }
        }

        private static void WriteViolationsToLog( string functionNameToLog, DataSet dataSetWithViolations)
        {
            StringBuilder violationsToLog = new StringBuilder();
            
            violationsToLog.AppendLine("Violations for Dataset: " + dataSetWithViolations.DataSetName);
            
            //Loop through all the tables.
            foreach (DataTable currentTable in dataSetWithViolations.Tables)
            {
                int rowCnt = 0;
                //Loop through all the rows.
                foreach (DataRow currentRow in currentTable.Rows)
                {
                    //Loop through all the column errors.
                    foreach (DataColumn columnInError in currentRow.GetColumnsInError())
                    {
                        //Log the error.
                        violationsToLog.AppendLine(string.Format(
                            "{0}: Table = {1}; Row Index = {2}; Column = {3}; Violation = {4}",
                            functionNameToLog, currentTable.TableName, 
                            rowCnt.ToString(), columnInError.ColumnName,
                            currentRow.GetColumnError(columnInError)));
                    }
                    //Increment the row count.
                    rowCnt++;
                }
            }

            //Log the violations.
            Logger.WriteLine(violationsToLog.ToString());
        }
    }

    internal static class Logger
    {
        internal static void WriteLine(String Message)
        {
            if (AppLogic.AppConfigBool("GoogleCheckout.LogMessages"))
            {
                try
                {
                    FileStream fs = new FileStream(Path.Combine(HttpContext.Current.Request.PhysicalApplicationPath, AppLogic.AppConfig("GoogleCheckout.LogFileName").Replace("/", "\\")), FileMode.Append, FileAccess.Write, FileShare.Read | FileShare.Delete);
                    StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);
                    sw.AutoFlush = true;
                    sw.WriteLine("{0:G}: {1}\r\n", DateTime.Now, Message);
                    sw.Close();
                    fs.Close();
                }
                catch { }
            }
        }
    }

    internal class CreditCardAuthorizationFacadeWrapper : CreditCardAuthorizationFacade
    {
        internal CreditCardAuthorizationFacadeWrapper()
            : base(null)
        {
        }

        public void ConvertCardPaymentToReceiptWrapper(string[] capturedAndForced, ReceiptDatasetGateway gatewayReceipt)
        {
            base.ConvertCardPaymentToReceipt(capturedAndForced, gatewayReceipt);
        }

        public bool AuthorizeCreditCardWrapper(ICreditCardGatewayInterface gateway, IReceiptInterface facadeReceipt, DataRow rowPaymentMethod, string transType, ref string result)
        {
            return base.AuthorizeCreditCard(gateway, facadeReceipt, rowPaymentMethod, transType, ref result);
        }
    }

    internal class GoogleCreditCardGateway : Interprise.Facade.Base.Customer.CreditCardGateway.BaseCreditCardGatewayFacade
    {
        private string m_responseAVS;
        private string m_responseCVV2;
        private string m_responseTransactionCode;
        private string m_responseData;
        private string m_referenceCode;
        private string m_responseApprovalCode;
        private Interprise.Framework.Base.Shared.Structure.ParentChildRelationInfo[] m_parentChildRelations;
        private string m_groupCode, m_formMenuCode;
        private string[][] m_customUpdateCommandSet;
        private string[][] m_customLoadCommandSet;

        internal GoogleCreditCardGateway(string googleOrderNumber, string authorizationResult,
            string authCode, string avs, string cv)
        {
            m_responseAVS = avs;
            m_responseCVV2 = cv;
            m_responseTransactionCode = googleOrderNumber;
            m_referenceCode = googleOrderNumber;
            m_responseData = authorizationResult;
            m_responseApprovalCode = authCode;
        }

        #region ICreditCardGatewayInterface Members

        public override bool Authorize(DataRow rowPaymentMethod, Interprise.Framework.Base.Shared.Enum.CreditCardTransaction transactionType, ref string result)
        {
            this.PaymentModifiedTransType = transactionType;
            return true;
        }

        public override bool IsTransactionSupported(Interprise.Framework.Base.Shared.Enum.CreditCardTransaction transactionType)
        {
            return true;
        }

        public override string ReferenceCode
        {
            get
            {
                return m_referenceCode;
            }
        }

        public override string ResponseAVS
        {
            get
            {
                return m_responseAVS;
            }
        }

        public override string ResponseApprovalCode
        {
            get
            {
                return m_responseApprovalCode;
            }
        }

        public override string ResponseCVV2
        {
            get
            {
                return m_responseCVV2;
            }
        }

        public override string ResponseCode
        {
            get
            {
                return "0";
            }
        }

        public override string ResponseData
        {
            get
            {
                return m_responseData;
            }
        }

        public override string ResponseText
        {
            get
            {
                return "Google Checkout";
            }
        }

        public override string ResponseTransactionCode
        {
            get
            {
                return m_responseTransactionCode;
            }
        }

        #endregion

        public string GroupCode
        {
            get { return m_groupCode; }
            set { m_groupCode = value; }
        }
        
        public string[][] CustomUpdateCommandSet
        {
            get { return m_customUpdateCommandSet; }
            set { m_customUpdateCommandSet = value; }
        }
        
        public string[][] CustomLoadCommandSet
        {
            get { return m_customLoadCommandSet; }
            set { m_customLoadCommandSet = value; }
        }
        
        public Structure.ParentChildRelationInfo[] ParentChildRelations
        {
            get { return m_parentChildRelations; }
            set { m_parentChildRelations = value; }
        }

        public string FormMenuCode
        {
            get { return m_formMenuCode; }
            set { m_formMenuCode = value; }
        }

        public string PostDocument(string userCode, string batchCode, Interprise.Framework.Base.Shared.Enum.DocumentType documentType, string documentCode, int commandTimeOut)
        {
            throw new NotImplementedException();
        }
    }
    
}











